Compare commits

..
Author SHA1 Message Date
Meier Lukas 0513a8e6f5 feat(integrations): add suggestions to manage home page 2025-06-12 22:36:37 +02:00
homarr-renovate[bot] 0b200dc9e2 fix(deps): update dependency zod to ^3.25.62 (#3382)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-11 19:19:05 +00:00
homarr-renovate[bot] 377f4302c0 chore(deps): update dependency @types/react to v19.1.8 (#3381)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-11 14:20:18 +00:00
homarr-crowdin[bot] 740e9a5082 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-06-11 12:05:38 +00:00
homarr-renovate[bot] e7652ea4eb fix(deps): update dependency tsx to v4.20.1 (#3380)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-11 09:23:20 +00:00
homarr-renovate[bot] 863f935ef4 fix(deps): update dependency zod to ^3.25.61 (#3379)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-11 08:27:30 +00:00
homarr-renovate[bot] 7388c907a5 fix(deps): update dependency zod to ^3.25.60 (#3378)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-11 07:22:06 +00:00
homarr-renovate[bot] 3d07306e7d fix(deps): update dependency tsx to v4.20.0 (#3375)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-11 03:21:10 +00:00
homarr-renovate[bot] f37fc0edfe fix(deps): update dependency zod to ^3.25.58 (#3373)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 23:21:00 +00:00
Meier Lukas c59cdeda47 fix: csp prevents external videos (#3372) 2025-06-10 22:14:43 +02:00
homarr-renovate[bot] ad444e8fa7 fix(deps): update testcontainers-node monorepo to ^11.0.3 (#3369)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 18:41:04 +00:00
homarr-renovate[bot] fa52b55828 fix(deps): update dependency swagger-ui-react to ^5.24.1 (#3368)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 06:28:24 +00:00
homarr-renovate[bot] 1ce1113a8c chore(deps): update dependency @vitejs/plugin-react to ^4.5.2 (#3366)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 04:30:15 +00:00
homarr-renovate[bot] e7a9797484 fix(deps): update dependency zod to ^3.25.57 (#3367)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 03:23:52 +00:00
homarr-renovate[bot] d104aea8b8 chore(deps): update dependency @types/node to ^22.15.31 (#3365)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 03:22:22 +00:00
homarr-renovate[bot] ab3ee3bac1 fix(deps): update dependency @t3-oss/env-nextjs to ^0.13.8 (#3364)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-10 01:18:38 +00:00
homarr-crowdin[bot] 838066ac75 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-06-10 00:52:45 +00:00
homarr-renovate[bot] b1a559d801 fix(deps): update dependency sass to ^1.89.2 (#3362)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-09 20:24:11 +00:00
homarr-renovate[bot] eecf50d461 chore(deps): update dependency @types/react to v19.1.7 (#3361)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-09 20:23:08 +00:00
homarr-renovate[bot] d862ae03e3 fix(deps): update mantine monorepo to ^8.1.0 (#3360)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-09 18:28:49 +00:00
homarr-renovate[bot] f6da091577 fix(deps): update dependency typescript-eslint to ^8.34.0 (#3359)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-09 18:28:06 +00:00
homarr-renovate[bot] 22f0cd885e fix(deps): update dependency @t3-oss/env-nextjs to ^0.13.7 (#3357)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-09 16:25:59 +00:00
homarr-renovate[bot] 08c6ce9071 chore(deps): update vitest monorepo to ^3.2.3 (#3356)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-09 12:36:15 +00:00
homarr-renovate[bot] c3e1a53d09 chore(deps): update pnpm to v10.12.1 (#3354)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-08 15:20:30 +00:00
homarr-renovate[bot] 9e86de6e17 fix(deps): update dependency fast-xml-parser to ^5.2.5 (#3353)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-08 08:25:10 +00:00
homarr-renovate[bot] 98c4cd1e39 fix(deps): update testcontainers-node monorepo to ^11.0.2 (#3352)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-07 20:21:39 +00:00
Manuel f67702e1d3 feat: reduce cron job intervals (#3235) 2025-06-07 17:32:39 +00:00
Meier Lukas a9e68e13d5 fix(pi-hole): cached session not invalidated when changing password (#3351) 2025-06-07 16:20:21 +02:00
Meier Lukas 1b3770aafb fix: module not found sshcrypto.node during development (#3349) 2025-06-07 16:19:09 +02:00
homarr-renovate[bot] 7928f15452 chore(deps): update dependency @types/dockerode to ^3.3.40 (#3347)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-07 10:21:45 +00:00
Meier Lukas 80327337ce chore(issue-template): add version 1.23.0 2025-06-07 12:11:33 +02:00
homarr-crowdin[bot] f72a994fb3 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-06-07 10:08:14 +00:00
homarr-renovate[bot] 1910786f5b chore(deps): update dependency @types/cookies to v0.9.1 (#3346)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-07 03:16:21 +00:00
homarr-renovate[bot] 6ec66b1fe2 fix(deps): update testcontainers-node monorepo to ^11.0.1 (#3343)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-06 21:43:01 +00:00
homarr-renovate[bot] aafd883c32 fix(deps): update dependency zod to ^3.25.56 (#3340)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-06 21:47:12 +02:00
Releases Homarr f32242bf79 chore(release): 1.23.0 [skip ci] 2025-06-06 19:16:16 +00:00
homarr-releases[bot] 62aeb043b5 chore(release): automatic release v1.23.0 2025-06-06 19:15:09 +00:00
Kaotic3andMeier Lukas ffd2f802bb fix(nextcloud): correct calendar event date mapping and support showU… (#3330)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-06-06 20:00:22 +02:00
lthomsen 3e7bf62500 fix: Allow nullable on cpuModelName and default to "Unknown CPU" if null in OMV Integration (#3310) 2025-06-06 20:00:09 +02:00
Andre Silva 7071d76c52 feat(releases-widget): add import from docker functionality (#3130) 2025-06-06 19:59:46 +02:00
Meier Lukas c7a21b1a5f fix: csp prevents external stylesheets (#3337) 2025-06-06 19:58:37 +02:00
homarr-renovate[bot] 6274a66048 fix(deps): update tiptap monorepo to v2.14.0 (#3334)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-06 15:20:46 +00:00
homarr-renovate[bot] 07f81cee92 fix(deps): update dependency dockerode to ^4.0.7 (#3331)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-06 15:19:53 +00:00
homarr-renovate[bot] 3ad72e63b1 fix(deps): update dependency fast-xml-parser to ^5.2.4 (#3336)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-06 14:21:15 +00:00
homarr-renovate[bot] ed3f848e8a fix(deps): update dependency zod to ^3.25.55 (#3332)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-06 08:26:48 +00:00
homarr-renovate[bot] 8484daaf75 fix(deps): update trpc monorepo to ^11.3.1 (#3329)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-05 21:20:00 +00:00
homarr-renovate[bot] aeed7dbfd0 chore(deps): update dependency @types/node to ^22.15.30 (#3328)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-05 19:18:20 +00:00
homarr-renovate[bot] 68881e7ee5 chore(deps): update vitest monorepo to ^3.2.2 (#3327)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-05 14:22:21 +00:00
homarr-renovate[bot] 61553cfc8e fix(deps): update tanstack-query monorepo to ^5.80.6 (#3326)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-05 13:35:26 +00:00
homarr-renovate[bot] cf7336ba87 fix(deps): update dependency swagger-ui-react to ^5.24.0 (#3325)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-05 10:23:04 +00:00
homarr-renovate[bot] dac82347e8 fix(deps): update tiptap monorepo to v2.13.0 (#3323)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-05 07:21:55 +00:00
homarr-renovate[bot] 74aa561f47 fix(deps): update trpc monorepo to ^11.3.0 (#3322)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 20:19:56 +00:00
homarr-renovate[bot] cb77a7dc6d fix(deps): update tanstack-query monorepo to ^5.80.5 (#3321)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 19:18:58 +00:00
homarr-renovate[bot] 81509fd96d fix(deps): update dependency video.js to ^8.23.3 (#3320)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 17:20:30 +00:00
homarr-renovate[bot] 960dc33af8 chore(deps): update dependency @types/react-dom to v19.1.6 (#3319)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 13:37:17 +00:00
homarr-renovate[bot] c051a42791 fix(deps): update tanstack-query monorepo to ^5.80.3 (#3317)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 10:22:38 +00:00
homarr-renovate[bot] a542ee0798 fix(deps): update dependency zod to ^3.25.51 (#3316)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 08:26:44 +00:00
homarr-renovate[bot] 9ac9042564 fix(deps): update dependency drizzle-orm to ^0.44.2 (#3315)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-04 07:21:31 +00:00
homarr-crowdin[bot] 2fe3dcb01b chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-06-04 00:52:41 +00:00
homarr-renovate[bot] a0a0449c70 fix(deps): update dependency zod to ^3.25.50 (#3312)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-03 22:21:28 +00:00
homarr-renovate[bot] d4afafe0f7 chore(deps): update vitest monorepo to ^3.2.1 (#3309)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-03 17:21:20 +00:00
homarr-renovate[bot] cae0cad92f fix(deps): update tanstack-query monorepo to ^5.80.2 (#3308)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-03 12:35:44 +00:00
homarr-renovate[bot] df2a9a5329 fix(deps): update tanstack-query monorepo to ^5.80.0 (#3307)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-03 10:23:13 +00:00
homarr-renovate[bot] 8f6cc6fa60 chore(deps): update dependency @vitejs/plugin-react to ^4.5.1 (#3306)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-03 04:28:26 +00:00
homarr-crowdin[bot] d699aad529 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-06-03 00:53:41 +00:00
homarr-renovate[bot] 0362487156 fix(deps): update dependency zod to ^3.25.49 (#3304)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 23:21:48 +00:00
homarr-renovate[bot] 5b354aca20 fix(deps): update dependency @tabler/icons-react to ^3.34.0 (#3303)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 18:27:59 +00:00
homarr-renovate[bot] 124e8de6c5 fix(deps): update dependency typescript-eslint to ^8.33.1 (#3302)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 18:27:20 +00:00
homarr-renovate[bot] a0585e3dd7 fix(deps): update tanstack-query monorepo to ^5.79.2 (#3301)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 17:20:23 +00:00
homarr-renovate[bot] a3deed3b09 fix(deps): update tanstack-query monorepo to ^5.79.1 (#3300)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 16:24:59 +00:00
homarr-renovate[bot] ae5aebda20 chore(deps): update pnpm to v10.11.1 (#3299)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 14:20:59 +00:00
homarr-renovate[bot]andMeier Lukas 362c663060 chore(deps): update vitest monorepo to ^3.2.0 (#3298)
* chore(deps): update vitest monorepo to ^3.2.0

* fix: reduce flakyness of nzbget test

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-06-02 12:07:37 +00:00
homarr-renovate[bot]andMeier Lukas 0d1e3a625a fix(deps): update testcontainers-node monorepo to v11 (major) (#3250)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-06-02 13:58:49 +02:00
homarr-renovate[bot] ccd8a85e0f fix(deps): update dependency zod to ^3.25.48 (#3297)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 11:17:12 +00:00
homarr-renovate[bot] f0ba693077 fix(deps): update trpc monorepo to ^11.2.0 (#3293)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 07:22:04 +00:00
homarr-renovate[bot] 94f176d544 fix(deps): update dependency zod to ^3.25.47 (#3296)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-02 06:28:59 +00:00
Meier Lukas a86fc5bcb8 chore(pr-template): add task for updating documentation 2025-06-01 10:43:32 +02:00
Meier Lukas 9bdbe6f425 chore(issue-template): add version 1.22.0 2025-06-01 10:40:32 +02:00
homarr-renovate[bot] 1b1671e0e6 fix(deps): update dependency zod to ^3.25.46 (#3292)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-01 08:24:52 +00:00
homarr-renovate[bot] 3337cfa3c7 fix(deps): update dependency zod to ^3.25.45 (#3291)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-06-01 03:35:28 +00:00
homarr-renovate[bot] 161fc86770 fix(deps): update dependency zod to ^3.25.43 (#3290)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-31 23:20:35 +00:00
homarr-renovate[bot] 76ae423fd9 chore(deps): update dependency prettier-plugin-packagejson to ^2.5.15 (#3289)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-31 12:32:21 +00:00
homarr-renovate[bot] 1f408685e6 fix(deps): update dependency node-cron to ^4.1.0 (#3286)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-31 03:14:01 +00:00
homarr-renovate[bot] b608ffe490 fix(deps): update dependency sass to ^1.89.1 (#3285)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-31 01:14:48 +00:00
homarr-crowdin[bot] f579b283f2 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-05-31 00:50:38 +00:00
homarr-renovate[bot] 9518ebb088 chore(deps): update dependency eslint to ^9.28.0 (#3283)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 21:20:21 +00:00
homarr-renovate[bot] 9895c1b3ff fix(deps): update dependency drizzle-orm to ^0.44.1 (#3280)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 20:22:34 +00:00
homarr-renovate[bot] 7e5ca418cc chore(deps): update dependency @types/node to ^22.15.29 (#3281)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 19:24:39 +00:00
Releases Homarr 6b76d55de2 chore(release): 1.22.0 [skip ci] 2025-05-30 19:17:12 +00:00
homarr-releases[bot] 09f821b95d chore(release): automatic release v1.22.0 2025-05-30 19:14:45 +00:00
Meier Lukas 11149348fc fix(cluster-health): storage items multiply (#3206) 2025-05-30 20:52:51 +02:00
homarr-renovate[bot] c8202ab3c6 fix(deps): update turbo monorepo to ^2.5.4 (#3279)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 16:24:36 +00:00
homarr-renovate[bot] 7a50829b6a chore(deps): update dependency @types/node to ^22.15.28 (#3277)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 15:21:51 +00:00
homarr-update-contributors[bot] 778cf861cf chore: update contributors (#3275)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2025-05-30 12:35:10 +00:00
homarr-renovate[bot] 6b8ffdfbc0 chore(deps): update dependency @types/node to ^22.15.27 (#3273)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 09:22:10 +00:00
homarr-renovate[bot] 608280e670 fix(deps): update dependency zod to ^3.25.42 (#3274)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 08:26:24 +00:00
homarr-renovate[bot] 5bf20aa353 fix(deps): update dependency zod to ^3.25.41 (#3272)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-30 03:15:18 +00:00
homarr-renovate[bot] 3ba9502b26 chore(deps): update dependency @types/node to ^22.15.26 (#3271)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 23:20:36 +00:00
homarr-renovate[bot] 862d07db68 fix(deps): update dependency zod to ^3.25.39 (#3270)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 22:21:03 +00:00
homarr-renovate[bot] d72a804ad9 fix(deps): update nextjs monorepo to v15.3.3 (#3269)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 20:24:32 +00:00
homarr-renovate[bot] 2320d3c567 fix(deps): update dependency flag-icons to ^7.5.0 (#3268)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 18:27:12 +00:00
homarr-renovate[bot] a0eda74fa4 fix(deps): update dependency flag-icons to ^7.4.0 (#3265)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 17:19:46 +00:00
homarr-renovate[bot] bac4f64257 fix(deps): update dependency ldapts to v8.0.1 (#3264)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 17:19:30 +00:00
homarr-renovate[bot] 1f9983f16d fix(deps): update tanstack-query monorepo to ^5.79.0 (#3263)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 12:23:07 +00:00
homarr-renovate[bot] 48ce2af4c1 fix(deps): update dependency zod to ^3.25.36 (#3262)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 08:26:55 +00:00
homarr-renovate[bot] 635c9a2687 fix(deps): update dependency @kubernetes/client-node to ^1.3.0 (#3261)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-29 01:15:13 +00:00
homarr-renovate[bot] c1324eab76 fix(deps): update dependency zod to ^3.25.34 (#3259)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-28 23:20:41 +00:00
homarr-renovate[bot] bfd7b70618 fix(deps): update trpc monorepo to ^11.1.4 (#3260)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-28 22:20:43 +00:00
homarr-renovate[bot] ad5f985645 chore(deps): update dependency @types/node to ^22.15.24 (#3257)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-28 21:20:37 +00:00
homarr-renovate[bot] 4a184d23f2 fix(deps): update dependency @ianvs/prettier-plugin-sort-imports to ^4.4.2 (#3258)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-28 20:23:40 +00:00
homarr-renovate[bot] 09b49a242d fix(deps): update dependency drizzle-orm to ^0.44.0 (#3256)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-28 15:22:07 +00:00
Meier Lukas 10f720d74c fix(search): removing server search engine causes loading error (#3252) 2025-05-28 07:57:37 +02:00
homarr-renovate[bot] 9c3a46c071 fix(deps): update dependency zod to ^3.25.32 (#3255)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-28 03:20:53 +00:00
homarr-renovate[bot] e2e4656001 fix(deps): update dependency zod to ^3.25.31 (#3254)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 22:20:28 +00:00
homarr-renovate[bot] 3253b2f138 fix(deps): update dependency octokit to ^5.0.3 (#3249)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 20:22:31 +00:00
homarr-renovate[bot] f9c7e7711a chore(deps): update dependency @types/node to ^22.15.23 (#3248)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 19:18:31 +00:00
homarr-renovate[bot] 8b553b80e6 fix(deps): update dependency typescript-eslint to ^8.33.0 (#3247)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 18:26:02 +00:00
homarr-renovate[bot] 75ebf075a5 chore(deps): update dependency @types/node to ^22.15.22 (#3246)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 18:25:39 +00:00
homarr-crowdin[bot] 1575716f41 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-05-27 17:02:51 +00:00
homarr-renovate[bot] 8966e2ad8a fix(deps): update dependency @t3-oss/env-nextjs to ^0.13.6 (#3244)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 12:36:02 +00:00
homarr-renovate[bot] 816544a996 fix(deps): update mantine monorepo to ^8.0.2 (#3245)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 11:19:17 +00:00
homarr-renovate[bot] 9c323850bc fix(deps): update dependency jotai to ^2.12.5 (#3243)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 09:21:29 +00:00
homarr-renovate[bot] c3936237c0 chore(deps): update dependency @types/react to v19.1.6 (#3242)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 08:26:07 +00:00
homarr-renovate[bot] 9b8d1cbb66 chore(deps): update dependency esbuild to ^0.25.5 (#3241)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 04:27:15 +00:00
homarr-renovate[bot] 3062948939 fix(deps): update dependency zod to ^3.25.30 (#3239)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 03:13:49 +00:00
homarr-renovate[bot] 78f98edc41 fix(deps): update trpc monorepo to ^11.1.3 (#3240)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-27 01:14:03 +00:00
homarr-renovate[bot] 81ca6ef7b2 fix(deps): update dependency zod to ^3.25.29 (#3237)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-26 23:20:16 +00:00
homarr-renovate[bot] 71186908b9 fix(deps): update tanstack-query monorepo to ^5.77.2 (#3232)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-26 12:34:40 +00:00
Meier Lukas cbbf0c8dcf fix: navigating back results in navigation back to first page opened (#3224) 2025-05-26 11:41:09 +02:00
Meier Lukas 1d27da2230 fix(calendar): remove scrollbar for 2x2 (#3227) 2025-05-26 11:40:42 +02:00
homarr-crowdin[bot] d4ef212042 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-05-26 00:53:16 +00:00
homarr-renovate[bot] 5ea7a20677 fix(deps): update tanstack-query monorepo to ^5.77.1 (#3229)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-25 22:19:07 +00:00
Meier Lukas 05e159f566 fix: csp prevents fonts to load (#3225) 2025-05-25 16:39:20 +02:00
homarr-renovate[bot] 6d9e42cf5d chore(deps): update dependency @semantic-release/github to ^11.0.3 (#3226)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-25 13:28:48 +00:00
Meier Lukas 437ecd1023 fix(calendar): load events for all visible days of calendar month (#3216) 2025-05-25 14:51:26 +02:00
Meier Lukas 85e98d2b60 fix(boards): app select modal images rendering issue (#3218) 2025-05-25 14:51:13 +02:00
homarr-crowdin[bot] 8e24d8362e chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-05-25 07:49:01 +00:00
Manuel c367f8ac23 docs: add pika pods (#3215) 2025-05-24 18:53:04 +02:00
Meier Lukas c67916f72e fix(downloads): upspeed font size wrong (#3214) 2025-05-24 18:52:55 +02:00
Meier Lukas 25bd3879db fix(media-request-stats): users squeezed together (#3213) 2025-05-24 18:52:45 +02:00
Meier Lukas 939df8f6d1 feat(cluster-health): add visibility options (#3210) 2025-05-24 17:49:49 +02:00
Meier Lukas 2dc871e531 feat(downloads): add option to limit amount of items (#3205) 2025-05-24 17:49:39 +02:00
Meier Lukas f7e5e823d5 fix(visual): check for creator board permission is wrong (#3212) 2025-05-24 17:49:08 +02:00
homarr-renovate[bot] 240bf16806 chore(deps): update dependency conventional-changelog-conventionalcommits to v9 (#3151)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-24 11:17:16 +02:00
homarr-renovate[bot] 640bf7b939 fix(deps): update dependency trpc-to-openapi to ^2.3.1 (#3046)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-24 09:14:40 +00:00
homarr-renovate[bot] ec42f265a0 fix(deps): update tanstack-query monorepo to ^5.77.0 (#3204)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-24 08:22:43 +00:00
homarr-crowdin[bot] ea5a763046 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-05-24 00:49:31 +00:00
homarr-renovate[bot] e0a9e4f97c fix(deps): update dependency zod to ^3.25.28 (#3201)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-23 23:20:33 +00:00
homarr-renovate[bot] 581746b2c2 chore(deps): update dependency semantic-release to ^24.2.5 (#3202)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-23 22:20:55 +00:00
ManuelandMeier Lukas be8b332f94 fix: reduce logging (#3200)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-05-23 22:36:23 +02:00
homarr-renovate[bot]andManuel 109f9e7979 fix(deps): update dependency node-cron to v4 (#3075)
Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
2025-05-23 22:20:47 +02:00
Meier Lukas 2afa9b6727 perf: cache middleware onboarding and language checks (#3197) 2025-05-23 22:20:21 +02:00
09af0a76f6 feat: allow custom protocols for app href (#2575)
Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-05-23 20:19:59 +00:00
Manuel 71516e02c0 ci: ignore auto gen and build files eslint (#3198) 2025-05-23 19:47:44 +00:00
Meier Lukas a5f7565468 chore(issue-template): add version 1.21.0 2025-05-23 21:19:35 +02:00
homarr-renovate[bot] 0b224e8835 fix(deps): update dependency zod to ^3.25.24 (#3195)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-05-23 19:18:29 +00:00
139 changed files with 5518 additions and 3203 deletions

No files matched your search

+3
View File
@@ -31,6 +31,9 @@ body:
label: Version
description: What version of Homarr are you running?
options:
- 1.23.0
- 1.22.0
- 1.21.0
- 1.20.0
- 1.19.1
- 1.19.0
+4 -4
View File
@@ -6,8 +6,8 @@
**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**
- [ ] Builds without warnings or errors (``pnpm build``, autofix with ``pnpm format:fix``)
- [ ] Pull request targets ``dev`` branch
- [ ] Builds without warnings or errors (`pnpm build`, autofix with `pnpm format:fix`)
- [ ] Pull request targets `dev` branch
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
- [ ] No shorthand variable names are used (eg. ``x``, ``y``, ``i`` or any abbrevation)
- [ ] No shorthand variable names are used (eg. `x`, `y`, `i` or any abbrevation)
- [ ] Documentation is up to date. Create a pull request [here](https://github.com/homarr-labs/documentation/).
+102
View File
@@ -1,3 +1,105 @@
## [1.23.0](https://github.com/homarr-labs/homarr/compare/v1.22.0...v1.23.0) (2025-06-06)
### Features
* **releases-widget:** add `import from docker` functionality ([#3130](https://github.com/homarr-labs/homarr/issues/3130)) ([7071d76](https://github.com/homarr-labs/homarr/commit/7071d76c526f1b59a955abcc595894975d574218))
### Bug Fixes
* Allow nullable on cpuModelName and default to "Unknown CPU" if null in OMV Integration ([#3310](https://github.com/homarr-labs/homarr/issues/3310)) ([3e7bf62](https://github.com/homarr-labs/homarr/commit/3e7bf62500f4ce0a8662104e2a656ea98cf2558f))
* csp prevents external stylesheets ([#3337](https://github.com/homarr-labs/homarr/issues/3337)) ([c7a21b1](https://github.com/homarr-labs/homarr/commit/c7a21b1a5f4e7c9d994d81b75a3fd4fa7c013daf))
* **deps:** update dependency @tabler/icons-react to ^3.34.0 ([#3303](https://github.com/homarr-labs/homarr/issues/3303)) ([5b354ac](https://github.com/homarr-labs/homarr/commit/5b354aca2047e8316b331e327cf6031690dcfef9))
* **deps:** update dependency dockerode to ^4.0.7 ([#3331](https://github.com/homarr-labs/homarr/issues/3331)) ([07f81ce](https://github.com/homarr-labs/homarr/commit/07f81cee92627e3b6ee4b2bbf084657f9a83ac34))
* **deps:** update dependency drizzle-orm to ^0.44.1 ([#3280](https://github.com/homarr-labs/homarr/issues/3280)) ([9895c1b](https://github.com/homarr-labs/homarr/commit/9895c1b3ff028a7ea7273ab2625620c9132f5213))
* **deps:** update dependency drizzle-orm to ^0.44.2 ([#3315](https://github.com/homarr-labs/homarr/issues/3315)) ([9ac9042](https://github.com/homarr-labs/homarr/commit/9ac90425647bf1f10d3f299f043c5d40067347bc))
* **deps:** update dependency fast-xml-parser to ^5.2.4 ([#3336](https://github.com/homarr-labs/homarr/issues/3336)) ([3ad72e6](https://github.com/homarr-labs/homarr/commit/3ad72e63b1adecd18c53897a6023f09d6f5d011c))
* **deps:** update dependency node-cron to ^4.1.0 ([#3286](https://github.com/homarr-labs/homarr/issues/3286)) ([1f40868](https://github.com/homarr-labs/homarr/commit/1f408685e69397fa9e3a69bd4331a8240da3713b))
* **deps:** update dependency sass to ^1.89.1 ([#3285](https://github.com/homarr-labs/homarr/issues/3285)) ([b608ffe](https://github.com/homarr-labs/homarr/commit/b608ffe4900a5dd97ceb2669464c477404d090e7))
* **deps:** update dependency swagger-ui-react to ^5.24.0 ([#3325](https://github.com/homarr-labs/homarr/issues/3325)) ([cf7336b](https://github.com/homarr-labs/homarr/commit/cf7336ba87c617578ab52e2ece94fd84df3fc623))
* **deps:** update dependency typescript-eslint to ^8.33.1 ([#3302](https://github.com/homarr-labs/homarr/issues/3302)) ([124e8de](https://github.com/homarr-labs/homarr/commit/124e8de6c584b0efca91d92079f8b3c400bccab2))
* **deps:** update dependency video.js to ^8.23.3 ([#3320](https://github.com/homarr-labs/homarr/issues/3320)) ([81509fd](https://github.com/homarr-labs/homarr/commit/81509fd96dfe0561964eb58d3d21c02400db2e8c))
* **deps:** update dependency zod to ^3.25.43 ([#3290](https://github.com/homarr-labs/homarr/issues/3290)) ([161fc86](https://github.com/homarr-labs/homarr/commit/161fc867703c4cf634eaaeec1b73f5e3cbfa7fec))
* **deps:** update dependency zod to ^3.25.45 ([#3291](https://github.com/homarr-labs/homarr/issues/3291)) ([3337cfa](https://github.com/homarr-labs/homarr/commit/3337cfa3c7e34debcccdbb95ee0bc39f2af98a91))
* **deps:** update dependency zod to ^3.25.46 ([#3292](https://github.com/homarr-labs/homarr/issues/3292)) ([1b1671e](https://github.com/homarr-labs/homarr/commit/1b1671e0e6d808469ba7f8185c4590cf2f2f6f44))
* **deps:** update dependency zod to ^3.25.47 ([#3296](https://github.com/homarr-labs/homarr/issues/3296)) ([94f176d](https://github.com/homarr-labs/homarr/commit/94f176d544272e0378e65255aba58b05d56019b2))
* **deps:** update dependency zod to ^3.25.48 ([#3297](https://github.com/homarr-labs/homarr/issues/3297)) ([ccd8a85](https://github.com/homarr-labs/homarr/commit/ccd8a85e0fcd2cf40dbfb6058787d24a02680b93))
* **deps:** update dependency zod to ^3.25.49 ([#3304](https://github.com/homarr-labs/homarr/issues/3304)) ([0362487](https://github.com/homarr-labs/homarr/commit/0362487156457ae825cac501e7d53db58c31ab0b))
* **deps:** update dependency zod to ^3.25.50 ([#3312](https://github.com/homarr-labs/homarr/issues/3312)) ([a0a0449](https://github.com/homarr-labs/homarr/commit/a0a0449c704ff57725ab5e0b459eb209fc929446))
* **deps:** update dependency zod to ^3.25.51 ([#3316](https://github.com/homarr-labs/homarr/issues/3316)) ([a542ee0](https://github.com/homarr-labs/homarr/commit/a542ee079871c7609ed97c76aa2017305adbfdc9))
* **deps:** update dependency zod to ^3.25.55 ([#3332](https://github.com/homarr-labs/homarr/issues/3332)) ([ed3f848](https://github.com/homarr-labs/homarr/commit/ed3f848e8afb57cc1b9249551f0260eb52bbd4a6))
* **deps:** update tanstack-query monorepo to ^5.79.1 ([#3300](https://github.com/homarr-labs/homarr/issues/3300)) ([a3deed3](https://github.com/homarr-labs/homarr/commit/a3deed3b090aed0df0f45a9bd04c006640691e05))
* **deps:** update tanstack-query monorepo to ^5.79.2 ([#3301](https://github.com/homarr-labs/homarr/issues/3301)) ([a0585e3](https://github.com/homarr-labs/homarr/commit/a0585e3dd7d9cfbccf8ff5535ae2c753a8b92764))
* **deps:** update tanstack-query monorepo to ^5.80.0 ([#3307](https://github.com/homarr-labs/homarr/issues/3307)) ([df2a9a5](https://github.com/homarr-labs/homarr/commit/df2a9a5329e06847904af56a0d058c0da59f6380))
* **deps:** update tanstack-query monorepo to ^5.80.2 ([#3308](https://github.com/homarr-labs/homarr/issues/3308)) ([cae0cad](https://github.com/homarr-labs/homarr/commit/cae0cad92f68f716887e077194d4e30402466d57))
* **deps:** update tanstack-query monorepo to ^5.80.3 ([#3317](https://github.com/homarr-labs/homarr/issues/3317)) ([c051a42](https://github.com/homarr-labs/homarr/commit/c051a42791de4eae4baf20360efe5b0c8925c22b))
* **deps:** update tanstack-query monorepo to ^5.80.5 ([#3321](https://github.com/homarr-labs/homarr/issues/3321)) ([cb77a7d](https://github.com/homarr-labs/homarr/commit/cb77a7dc6d60560250765d129aac9bd236693e2b))
* **deps:** update tanstack-query monorepo to ^5.80.6 ([#3326](https://github.com/homarr-labs/homarr/issues/3326)) ([61553cf](https://github.com/homarr-labs/homarr/commit/61553cfc8e48a1179a148007fb970b1f4e6d509c))
* **deps:** update testcontainers-node monorepo to v11 (major) ([#3250](https://github.com/homarr-labs/homarr/issues/3250)) ([0d1e3a6](https://github.com/homarr-labs/homarr/commit/0d1e3a625a04abd5f667640ab40e01d28e017488))
* **deps:** update tiptap monorepo to v2.13.0 ([#3323](https://github.com/homarr-labs/homarr/issues/3323)) ([dac8234](https://github.com/homarr-labs/homarr/commit/dac82347e86dd7faacb24adbfc61ace9c917b8cd))
* **deps:** update tiptap monorepo to v2.14.0 ([#3334](https://github.com/homarr-labs/homarr/issues/3334)) ([6274a66](https://github.com/homarr-labs/homarr/commit/6274a66048163513e3faaf7011d7d6aa6ad9f161))
* **deps:** update trpc monorepo to ^11.2.0 ([#3293](https://github.com/homarr-labs/homarr/issues/3293)) ([f0ba693](https://github.com/homarr-labs/homarr/commit/f0ba693077a0c7f7897c81f02ad66b77fce52f1c))
* **deps:** update trpc monorepo to ^11.3.0 ([#3322](https://github.com/homarr-labs/homarr/issues/3322)) ([74aa561](https://github.com/homarr-labs/homarr/commit/74aa561f470f0f76421f356998caa91dc8bb65a1))
* **deps:** update trpc monorepo to ^11.3.1 ([#3329](https://github.com/homarr-labs/homarr/issues/3329)) ([8484daa](https://github.com/homarr-labs/homarr/commit/8484daaf7516ad72dd9e7de3117b5e0d50415dbb))
* **nextcloud:** correct calendar event date mapping and support showU… ([#3330](https://github.com/homarr-labs/homarr/issues/3330)) ([ffd2f80](https://github.com/homarr-labs/homarr/commit/ffd2f802bb777f13eac1ee45e1b5f0ca2b596672))
## [1.22.0](https://github.com/homarr-labs/homarr/compare/v1.21.0...v1.22.0) (2025-05-30)
### Features
* allow custom protocols for app href ([#2575](https://github.com/homarr-labs/homarr/issues/2575)) ([09af0a7](https://github.com/homarr-labs/homarr/commit/09af0a76f64c2fec512ba30726827208d5c3ad95))
* **cluster-health:** add visibility options ([#3210](https://github.com/homarr-labs/homarr/issues/3210)) ([939df8f](https://github.com/homarr-labs/homarr/commit/939df8f6d11bb36f71854425e4fdc4ca0724da31))
* **downloads:** add option to limit amount of items ([#3205](https://github.com/homarr-labs/homarr/issues/3205)) ([2dc871e](https://github.com/homarr-labs/homarr/commit/2dc871e531b58aee25db26ab7c5e94626517db36))
### Bug Fixes
* **boards:** app select modal images rendering issue ([#3218](https://github.com/homarr-labs/homarr/issues/3218)) ([85e98d2](https://github.com/homarr-labs/homarr/commit/85e98d2b603abe18b37fa74bf50836422b72c9e1))
* **calendar:** load events for all visible days of calendar month ([#3216](https://github.com/homarr-labs/homarr/issues/3216)) ([437ecd1](https://github.com/homarr-labs/homarr/commit/437ecd10239c70e5a8c4d34119076ab155a2159e))
* **calendar:** remove scrollbar for 2x2 ([#3227](https://github.com/homarr-labs/homarr/issues/3227)) ([1d27da2](https://github.com/homarr-labs/homarr/commit/1d27da22303b2133bb1a7c59102b7c292bc34833))
* **cluster-health:** storage items multiply ([#3206](https://github.com/homarr-labs/homarr/issues/3206)) ([1114934](https://github.com/homarr-labs/homarr/commit/11149348fcbe9002bba33763e0b03aed50d20f71))
* csp prevents fonts to load ([#3225](https://github.com/homarr-labs/homarr/issues/3225)) ([05e159f](https://github.com/homarr-labs/homarr/commit/05e159f5662f04dd298eb13816fe1768d7ef9312))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.4.2 ([#3258](https://github.com/homarr-labs/homarr/issues/3258)) ([4a184d2](https://github.com/homarr-labs/homarr/commit/4a184d23f25a8a06b36cd8c6d9b2a65118399d9f))
* **deps:** update dependency @kubernetes/client-node to ^1.3.0 ([#3261](https://github.com/homarr-labs/homarr/issues/3261)) ([635c9a2](https://github.com/homarr-labs/homarr/commit/635c9a2687cf2b9a09a3093eb638ef772eddfb03))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.6 ([#3244](https://github.com/homarr-labs/homarr/issues/3244)) ([8966e2a](https://github.com/homarr-labs/homarr/commit/8966e2ad8a2025dfc91934ccf825bd76c08dc333))
* **deps:** update dependency drizzle-orm to ^0.44.0 ([#3256](https://github.com/homarr-labs/homarr/issues/3256)) ([09b49a2](https://github.com/homarr-labs/homarr/commit/09b49a242dd5d3d34bab758f190ab948b6fc7566))
* **deps:** update dependency flag-icons to ^7.4.0 ([#3265](https://github.com/homarr-labs/homarr/issues/3265)) ([a0eda74](https://github.com/homarr-labs/homarr/commit/a0eda74fa472446c5b93c1b65829e65670620ab6))
* **deps:** update dependency flag-icons to ^7.5.0 ([#3268](https://github.com/homarr-labs/homarr/issues/3268)) ([2320d3c](https://github.com/homarr-labs/homarr/commit/2320d3c5674413e18e9bb42fd004ada016c5e605))
* **deps:** update dependency jotai to ^2.12.5 ([#3243](https://github.com/homarr-labs/homarr/issues/3243)) ([9c32385](https://github.com/homarr-labs/homarr/commit/9c323850bc49df5f6fc3437678faab0c7eab2e53))
* **deps:** update dependency ldapts to v8.0.1 ([#3264](https://github.com/homarr-labs/homarr/issues/3264)) ([bac4f64](https://github.com/homarr-labs/homarr/commit/bac4f642574caf5f170d20353c036396698e5963))
* **deps:** update dependency node-cron to v4 ([#3075](https://github.com/homarr-labs/homarr/issues/3075)) ([109f9e7](https://github.com/homarr-labs/homarr/commit/109f9e79793b36b46ab1987a56f7c206e21d32e4))
* **deps:** update dependency octokit to ^5.0.3 ([#3249](https://github.com/homarr-labs/homarr/issues/3249)) ([3253b2f](https://github.com/homarr-labs/homarr/commit/3253b2f138a9e911b50c952e37fb4afb1f71446d))
* **deps:** update dependency trpc-to-openapi to ^2.3.1 ([#3046](https://github.com/homarr-labs/homarr/issues/3046)) ([640bf7b](https://github.com/homarr-labs/homarr/commit/640bf7b939b201d9905f5119af0dc0da6120b98c))
* **deps:** update dependency typescript-eslint to ^8.33.0 ([#3247](https://github.com/homarr-labs/homarr/issues/3247)) ([8b553b8](https://github.com/homarr-labs/homarr/commit/8b553b80e61ab608d01d890d26a8ea5bae3f38b8))
* **deps:** update dependency zod to ^3.25.24 ([#3195](https://github.com/homarr-labs/homarr/issues/3195)) ([0b224e8](https://github.com/homarr-labs/homarr/commit/0b224e8835d4fc5a292a624c8af8d874216b703a))
* **deps:** update dependency zod to ^3.25.28 ([#3201](https://github.com/homarr-labs/homarr/issues/3201)) ([e0a9e4f](https://github.com/homarr-labs/homarr/commit/e0a9e4f97c9dddb95fbc0b2a65acfd802dac9c19))
* **deps:** update dependency zod to ^3.25.29 ([#3237](https://github.com/homarr-labs/homarr/issues/3237)) ([81ca6ef](https://github.com/homarr-labs/homarr/commit/81ca6ef7b2767fb0bc285ffa53998e2c586225ec))
* **deps:** update dependency zod to ^3.25.30 ([#3239](https://github.com/homarr-labs/homarr/issues/3239)) ([3062948](https://github.com/homarr-labs/homarr/commit/30629489396668125dec7d93fcb0e733099c35fe))
* **deps:** update dependency zod to ^3.25.31 ([#3254](https://github.com/homarr-labs/homarr/issues/3254)) ([e2e4656](https://github.com/homarr-labs/homarr/commit/e2e46560010a94603852ea90a0fc63fabd473198))
* **deps:** update dependency zod to ^3.25.32 ([#3255](https://github.com/homarr-labs/homarr/issues/3255)) ([9c3a46c](https://github.com/homarr-labs/homarr/commit/9c3a46c071e169459ec7b8c9ba1647b4b354cbea))
* **deps:** update dependency zod to ^3.25.34 ([#3259](https://github.com/homarr-labs/homarr/issues/3259)) ([c1324ea](https://github.com/homarr-labs/homarr/commit/c1324eab76795dabc0a41315191f9ab6a73ba501))
* **deps:** update dependency zod to ^3.25.36 ([#3262](https://github.com/homarr-labs/homarr/issues/3262)) ([48ce2af](https://github.com/homarr-labs/homarr/commit/48ce2af4c182985a7721b65535bcf1b41f1cdcba))
* **deps:** update dependency zod to ^3.25.39 ([#3270](https://github.com/homarr-labs/homarr/issues/3270)) ([862d07d](https://github.com/homarr-labs/homarr/commit/862d07db68e7624342cb81241ef9924e6407fda8))
* **deps:** update dependency zod to ^3.25.41 ([#3272](https://github.com/homarr-labs/homarr/issues/3272)) ([5bf20aa](https://github.com/homarr-labs/homarr/commit/5bf20aa3538503de1d4f4d04f9de3e3b92442d75))
* **deps:** update dependency zod to ^3.25.42 ([#3274](https://github.com/homarr-labs/homarr/issues/3274)) ([608280e](https://github.com/homarr-labs/homarr/commit/608280e670432098ce466cfb8fa2b315bf35ead2))
* **deps:** update mantine monorepo to ^8.0.2 ([#3245](https://github.com/homarr-labs/homarr/issues/3245)) ([816544a](https://github.com/homarr-labs/homarr/commit/816544a996656ccf7cbacf76aff83d013ba21ad1))
* **deps:** update nextjs monorepo to v15.3.3 ([#3269](https://github.com/homarr-labs/homarr/issues/3269)) ([d72a804](https://github.com/homarr-labs/homarr/commit/d72a804ad9feb24a2b067712128a6566930990a6))
* **deps:** update tanstack-query monorepo to ^5.77.0 ([#3204](https://github.com/homarr-labs/homarr/issues/3204)) ([ec42f26](https://github.com/homarr-labs/homarr/commit/ec42f265a04f0f96105d6b1318becd5cc94aa8fb))
* **deps:** update tanstack-query monorepo to ^5.77.1 ([#3229](https://github.com/homarr-labs/homarr/issues/3229)) ([5ea7a20](https://github.com/homarr-labs/homarr/commit/5ea7a206772924faf1f842c80033954c853b3a02))
* **deps:** update tanstack-query monorepo to ^5.77.2 ([#3232](https://github.com/homarr-labs/homarr/issues/3232)) ([7118690](https://github.com/homarr-labs/homarr/commit/71186908b96f717c6f8c528b0f983e1792a7a885))
* **deps:** update tanstack-query monorepo to ^5.79.0 ([#3263](https://github.com/homarr-labs/homarr/issues/3263)) ([1f9983f](https://github.com/homarr-labs/homarr/commit/1f9983f16db8677b8c244b63306ea4afd2195d94))
* **deps:** update trpc monorepo to ^11.1.3 ([#3240](https://github.com/homarr-labs/homarr/issues/3240)) ([78f98ed](https://github.com/homarr-labs/homarr/commit/78f98edc4145e27e2daa269121173905c642c139))
* **deps:** update trpc monorepo to ^11.1.4 ([#3260](https://github.com/homarr-labs/homarr/issues/3260)) ([bfd7b70](https://github.com/homarr-labs/homarr/commit/bfd7b706182340eaa1db70a3c6cdcc935aee8df4))
* **deps:** update turbo monorepo to ^2.5.4 ([#3279](https://github.com/homarr-labs/homarr/issues/3279)) ([c8202ab](https://github.com/homarr-labs/homarr/commit/c8202ab3c676bf796c6e7aff2ebfef224a0124c8))
* **downloads:** upspeed font size wrong ([#3214](https://github.com/homarr-labs/homarr/issues/3214)) ([c67916f](https://github.com/homarr-labs/homarr/commit/c67916f72eded64133fd458184a599f32d8fc63c))
* **media-request-stats:** users squeezed together ([#3213](https://github.com/homarr-labs/homarr/issues/3213)) ([25bd387](https://github.com/homarr-labs/homarr/commit/25bd3879db9260bbd24a1338cc5f26652ed78d72))
* navigating back results in navigation back to first page opened ([#3224](https://github.com/homarr-labs/homarr/issues/3224)) ([cbbf0c8](https://github.com/homarr-labs/homarr/commit/cbbf0c8dcf685e31e70bb62f04b1065dab90e18d))
* reduce logging ([#3200](https://github.com/homarr-labs/homarr/issues/3200)) ([be8b332](https://github.com/homarr-labs/homarr/commit/be8b332f942beb3af181ee6f39558882ed640a0e))
* **search:** removing server search engine causes loading error ([#3252](https://github.com/homarr-labs/homarr/issues/3252)) ([10f720d](https://github.com/homarr-labs/homarr/commit/10f720d74c6cc70cbf217e8daf8cc7878ac6d712))
* **visual:** check for creator board permission is wrong ([#3212](https://github.com/homarr-labs/homarr/issues/3212)) ([f7e5e82](https://github.com/homarr-labs/homarr/commit/f7e5e823d5dee36ea11f243a0da7d84a1f363419))
### Performance Improvements
* cache middleware onboarding and language checks ([#3197](https://github.com/homarr-labs/homarr/issues/3197)) ([2afa9b6](https://github.com/homarr-labs/homarr/commit/2afa9b67274edd79b52598dc0d22437048a67e2c))
## [1.21.0](https://github.com/homarr-labs/homarr/compare/v1.20.0...v1.21.0) (2025-05-23)
### Features
+32 -19
View File
@@ -17,31 +17,16 @@ const withNextIntl = createNextIntlPlugin({
requestConfig: "../../packages/translation/src/request.ts",
});
interface WebpackConfig {
module: {
rules: {
test: RegExp;
loader: string;
}[];
};
}
const nextConfig: NextConfig = {
output: "standalone",
reactStrictMode: true,
/** We already do linting and typechecking as separate tasks in CI */
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
webpack: (config: WebpackConfig, { isServer }) => {
if (isServer) {
config.module.rules.push({
test: /\.node$/,
loader: "node-loader",
});
}
return config;
},
/**
* dockerode is required in the external server packages because of https://github.com/homarr-labs/homarr/issues/612
*/
serverExternalPackages: ["dockerode"],
experimental: {
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
},
@@ -49,6 +34,34 @@ const nextConfig: NextConfig = {
images: {
domains: ["cdn.jsdelivr.net"],
},
// eslint-disable-next-line @typescript-eslint/require-await,no-restricted-syntax
async headers() {
return [
{
source: "/(.*)", // Apply CSP to all routes
headers: [
{
key: "Content-Security-Policy",
value: `
default-src 'self';
script-src * 'unsafe-inline' 'unsafe-eval';
base-uri 'self';
connect-src *;
style-src * 'unsafe-inline';
frame-ancestors *;
frame-src *;
form-action 'self';
img-src * data:;
font-src * data:;
media-src * data:;
`
.replace(/\s{2,}/g, " ")
.trim(),
},
],
},
];
},
};
// Skip transform is used because of webpack loader, without it for example 'Tooltip.Floating' will not work and show an error
+24 -24
View File
@@ -48,21 +48,21 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"@mantine/colors-generator": "^8.0.1",
"@mantine/core": "^8.0.1",
"@mantine/dropzone": "^8.0.1",
"@mantine/hooks": "^8.0.1",
"@mantine/modals": "^8.0.1",
"@mantine/tiptap": "^8.0.1",
"@mantine/colors-generator": "^8.1.0",
"@mantine/core": "^8.1.0",
"@mantine/dropzone": "^8.1.0",
"@mantine/hooks": "^8.1.0",
"@mantine/modals": "^8.1.0",
"@mantine/tiptap": "^8.1.0",
"@million/lint": "1.0.14",
"@tabler/icons-react": "^3.33.0",
"@tanstack/react-query": "^5.76.2",
"@tanstack/react-query-devtools": "^5.76.2",
"@tanstack/react-query-next-experimental": "^5.76.2",
"@trpc/client": "^11.1.2",
"@trpc/next": "^11.1.2",
"@trpc/react-query": "^11.1.2",
"@trpc/server": "^11.1.2",
"@tabler/icons-react": "^3.34.0",
"@tanstack/react-query": "^5.80.6",
"@tanstack/react-query-devtools": "^5.80.6",
"@tanstack/react-query-next-experimental": "^5.80.6",
"@trpc/client": "^11.3.1",
"@trpc/next": "^11.3.1",
"@trpc/react-query": "^11.3.1",
"@trpc/server": "^11.3.1",
"@xterm/addon-canvas": "^0.7.0",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "^5.5.0",
@@ -70,35 +70,35 @@
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"dotenv": "^16.5.0",
"flag-icons": "^7.3.2",
"flag-icons": "^7.5.0",
"glob": "^11.0.2",
"jotai": "^2.12.4",
"jotai": "^2.12.5",
"mantine-react-table": "2.0.0-beta.9",
"next": "15.3.2",
"next": "15.3.3",
"postcss-preset-mantine": "^1.17.0",
"prismjs": "^1.30.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-error-boundary": "^6.0.0",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.89.0",
"sass": "^1.89.2",
"superjson": "2.2.2",
"swagger-ui-react": "^5.22.0",
"swagger-ui-react": "^5.24.1",
"use-deep-compare-effect": "^1.8.1",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"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.1",
"@types/node": "^22.15.21",
"@types/node": "^22.15.31",
"@types/prismjs": "^1.26.5",
"@types/react": "19.1.5",
"@types/react-dom": "19.1.5",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"@types/swagger-ui-react": "^5.18.0",
"concurrently": "^9.1.2",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"node-loader": "^2.1.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3"
@@ -1,6 +1,5 @@
"use client";
import type { MouseEvent } from "react";
import { useCallback, useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
@@ -204,17 +203,22 @@ const SelectBoardsMenu = () => {
);
};
const anchorSelector = "a[href]:not([target='_blank'])";
const usePreventLeaveWithDirty = (isDirty: boolean) => {
const t = useI18n();
const { openConfirmModal } = useConfirmModal();
const router = useRouter();
useEffect(() => {
const handleClick = (event: MouseEvent<HTMLElement>) => {
if (!isDirty) return;
const handleClick = (event: Event) => {
const target = (event.target as HTMLElement).closest("a");
if (!target) return;
if (!isDirty) return;
if (!target) {
console.warn("No anchor element found for click event", event);
return;
}
event.preventDefault();
@@ -231,33 +235,29 @@ const usePreventLeaveWithDirty = (isDirty: boolean) => {
};
const handlePopState = (event: Event) => {
if (isDirty) {
window.history.pushState(null, document.title, window.location.href);
event.preventDefault();
} else {
window.history.back();
}
window.history.pushState(null, document.title, window.location.href);
event.preventDefault();
};
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
if (!isDirty) return;
if (env.NODE_ENV === "development") return; // Allow to reload in development
event.preventDefault();
event.returnValue = true;
};
document.querySelectorAll("a").forEach((link) => {
link.addEventListener("click", handleClick as never);
const anchors = document.querySelectorAll(anchorSelector);
anchors.forEach((link) => {
link.addEventListener("click", handleClick);
});
window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
document.querySelectorAll("a").forEach((link) => {
link.removeEventListener("click", handleClick as never);
window.removeEventListener("popstate", handlePopState);
anchors.forEach((link) => {
link.removeEventListener("click", handleClick);
});
window.removeEventListener("popstate", handlePopState);
window.removeEventListener("beforeunload", handleBeforeUnload);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
+37 -1
View File
@@ -1,13 +1,16 @@
import Link from "next/link";
import { Card, Group, SimpleGrid, Space, Stack, Text } from "@mantine/core";
import { Card, Group, SimpleGrid, Space, Stack, Text, Title } from "@mantine/core";
import { IconArrowRight } from "@tabler/icons-react";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { getIntegrationName } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import { IntegrationAvatar } from "@homarr/ui";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { catchTrpcNotFound } from "~/errors/trpc-catch-error";
import { createMetaTitle } from "~/metadata";
import { HeroBanner } from "./_components/hero-banner";
@@ -30,6 +33,8 @@ export async function generateMetadata() {
export default async function ManagementPage() {
const statistics = await api.home.getStats();
const session = await auth();
const isAdmin = session?.user.permissions.includes("admin") ?? false;
const suggestions = isAdmin ? await api.integration.suggestedIntegrations().catch(catchTrpcNotFound) : [];
const t = await getScopedI18n("management.page.home");
const links: LinkProps[] = [
@@ -102,6 +107,37 @@ export default async function ManagementPage() {
),
)}
</SimpleGrid>
{suggestions.length >= 1 && (
<>
<Space h="md" />
<Stack gap="sm">
<Stack gap={0}>
<Title order={3}>Suggested integrations</Title>
<Text c="dimmed">Based on your Docker containers</Text>
</Stack>
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3 }}>
{suggestions.slice(0, 6).map((suggestion) => (
<Card key={suggestion.kind} component={Link} href="#" radius="lg">
<Group justify="space-between">
<Group>
<IntegrationAvatar kind={suggestion.kind} size="sm" />
<Stack gap={0}>
<Text size="md" fw={500}>
{getIntegrationName(suggestion.kind)}
</Text>
<Text c="dimmed" size="sm">
Show active media streams
</Text>
</Stack>
</Group>
<IconArrowRight />
</Group>
</Card>
))}
</SimpleGrid>
</Stack>
</>
)}
</>
);
}
+17 -6
View File
@@ -5,25 +5,36 @@ import SuperJSON from "superjson";
import type { AppRouter } from "@homarr/api";
import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared";
import { localeCookieKey } from "@homarr/definitions";
import type { SupportedLanguage } from "@homarr/translation";
import { supportedLanguages } from "@homarr/translation";
import { createI18nMiddleware } from "@homarr/translation/middleware";
export async function middleware(request: NextRequest) {
// fetch api does not work because window is not defined and we need to construct the url from the headers
// In next 15 we will be able to use node apis and such the db directly
const culture = await serverFetchApi.serverSettings.getCulture.query();
let isOnboardingFinished = false;
export async function middleware(request: NextRequest) {
// Redirect to onboarding if it's not finished yet
const pathname = request.nextUrl.pathname;
if (!pathname.endsWith("/init")) {
if (!isOnboardingFinished && !pathname.endsWith("/init")) {
const currentOnboardingStep = await serverFetchApi.onboard.currentStep.query();
if (currentOnboardingStep.current !== "finish") {
return NextResponse.redirect(new URL("/init", request.url));
}
isOnboardingFinished = true;
}
// Only run this if the user has not already configured their language
const currentLocale = request.cookies.get(localeCookieKey)?.value;
let defaultLocale: SupportedLanguage = "en";
if (!currentLocale || !supportedLanguages.includes(currentLocale as SupportedLanguage)) {
defaultLocale = await serverFetchApi.serverSettings.getCulture.query().then((culture) => culture.defaultLocale);
}
// We don't want to fallback to accept-language header so we clear it
request.headers.set("accept-language", "");
const next = createI18nMiddleware(culture.defaultLocale);
const next = createI18nMiddleware(defaultLocale);
return next(request);
}
+4 -4
View File
@@ -44,12 +44,12 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^22.15.21",
"@types/node": "^22.15.31",
"dotenv-cli": "^8.0.0",
"esbuild": "^0.25.4",
"eslint": "^9.27.0",
"esbuild": "^0.25.5",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"tsx": "4.19.4",
"tsx": "4.20.1",
"typescript": "^5.8.3"
}
}
+3 -3
View File
@@ -26,7 +26,7 @@
"@homarr/redis": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"dotenv": "^16.5.0",
"tsx": "4.19.4",
"tsx": "4.20.1",
"ws": "^8.18.2"
},
"devDependencies": {
@@ -34,8 +34,8 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/ws": "^8.18.1",
"esbuild": "^0.25.4",
"eslint": "^9.27.0",
"esbuild": "^0.25.5",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3"
}
+3 -1
View File
@@ -120,4 +120,6 @@ You can also support us by helping with [translating the entire project](https:/
Thanks to your generous sponsors we can continue to build Homarr. Check them out for high quality and easy to use development tools.
Feel free to contact us at homarr-labs@proton.me if you wish to become a sponsor.
[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=%5Bhomarr%5D&utm_campaign=oss)
[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=%5Bhomarr%5D&utm_campaign=oss) \
[![Supported by PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=homarr-v1)
+12 -12
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.21.0",
"version": "1.23.0",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -35,25 +35,25 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^11.0.2",
"@semantic-release/github": "^11.0.3",
"@semantic-release/npm": "^12.0.1",
"@semantic-release/release-notes-generator": "^14.0.3",
"@turbo/gen": "^2.5.3",
"@vitejs/plugin-react": "^4.5.0",
"@vitest/coverage-v8": "^3.1.4",
"@vitest/ui": "^3.1.4",
"conventional-changelog-conventionalcommits": "^8.0.0",
"@turbo/gen": "^2.5.4",
"@vitejs/plugin-react": "^4.5.2",
"@vitest/coverage-v8": "^3.2.3",
"@vitest/ui": "^3.2.3",
"conventional-changelog-conventionalcommits": "^9.0.0",
"cross-env": "^7.0.3",
"jsdom": "^26.1.0",
"prettier": "^3.5.3",
"semantic-release": "^24.2.4",
"testcontainers": "^10.28.0",
"turbo": "^2.5.3",
"semantic-release": "^24.2.5",
"testcontainers": "^11.0.3",
"turbo": "^2.5.4",
"typescript": "^5.8.3",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.1.4"
"vitest": "^3.2.3"
},
"packageManager": "pnpm@10.11.0",
"packageManager": "pnpm@10.12.1",
"engines": {
"node": ">=22.16.0"
},
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+10 -10
View File
@@ -40,25 +40,25 @@
"@homarr/request-handler": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@kubernetes/client-node": "^1.2.0",
"@tanstack/react-query": "^5.76.2",
"@trpc/client": "^11.1.2",
"@trpc/react-query": "^11.1.2",
"@trpc/server": "^11.1.2",
"@trpc/tanstack-react-query": "^11.1.2",
"@kubernetes/client-node": "^1.3.0",
"@tanstack/react-query": "^5.80.6",
"@trpc/client": "^11.3.1",
"@trpc/react-query": "^11.3.1",
"@trpc/server": "^11.3.1",
"@trpc/tanstack-react-query": "^11.3.1",
"lodash.clonedeep": "^4.5.0",
"next": "15.3.2",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"superjson": "2.2.2",
"trpc-to-openapi": "^2.2.0",
"zod": "^3.25.23"
"trpc-to-openapi": "^2.3.1",
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3"
}
@@ -14,15 +14,19 @@ import {
integrationUserPermissions,
searchEngines,
} from "@homarr/db/schema";
import type { IntegrationSecretKind } from "@homarr/definitions";
import type { IntegrationKind, IntegrationSecretKind } from "@homarr/definitions";
import {
getIconUrl,
getIntegrationKindsByCategory,
getIntegrationName,
getPermissionsWithParents,
integrationDefs,
integrationKinds,
integrationSecretKindObject,
} from "@homarr/definitions";
import type { ContainerInfo, Port } from "@homarr/docker";
import { DockerSingleton } from "@homarr/docker";
import { env } from "@homarr/docker/env";
import { createIntegrationAsync } from "@homarr/integrations";
import { logger } from "@homarr/log";
import { byIdSchema } from "@homarr/validation/common";
@@ -527,8 +531,169 @@ export const integrationRouter = createTRPCRouter({
const integrationInstance = await createIntegrationAsync(ctx.integration);
return await integrationInstance.searchAsync(encodeURI(input.query));
}),
suggestedIntegrations: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => {
if (!env.ENABLE_DOCKER)
throw new TRPCError({
code: "NOT_FOUND",
message: "Docker is not enabled",
});
const dockerInstances = DockerSingleton.getInstances();
if (dockerInstances.length === 0) {
throw new TRPCError({
code: "NOT_FOUND",
message: "No Docker instances found",
});
}
// TODO: remove those that were already added
const containers = await Promise.all(
dockerInstances.map(({ instance, host, isSocket }) =>
instance.listContainers().then((containers) =>
containers.map((containerInfo) => ({
info: containerInfo,
host,
isSocket,
})),
),
),
).then((results) => results.flat());
// Default docker hostname consist out of first 12 characters of the container id
const homarrContainer = containers.find((container) => container.info.Id.slice(0, 12) === process.env.HOSTNAME);
const suggestions = containers
.map((container) => getSuggestionFromContainer(container, homarrContainer))
.filter((suggestion) => suggestion !== null);
const groupedSuggestions = suggestions.reduce(
(acc, suggestion) => {
acc[suggestion.kind] ??= [];
// is defined above
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
acc[suggestion.kind]!.push(suggestion);
return acc;
},
{} as Partial<Record<IntegrationKind, IntegrationSuggestion[]>>,
);
return Object.entries(groupedSuggestions)
.map(([kind, suggestions]) => ({
kind: kind as IntegrationKind,
suggestions,
}))
.sort((itemA, itemB) => {
if (itemA.suggestions.length !== itemB.suggestions.length)
return itemB.suggestions.length - itemA.suggestions.length;
const createdAtA = Math.max(...itemA.suggestions.map((suggestion) => suggestion.createdAt?.getTime() ?? 0));
const createdAtB = Math.max(...itemB.suggestions.map((suggestion) => suggestion.createdAt?.getTime() ?? 0));
return createdAtB - createdAtA;
});
}),
});
interface IntegrationSuggestion {
containerId: string;
name: string;
internalUrl: string | null;
externalUrl: string | null;
kind: IntegrationKind;
createdAt?: Date;
}
interface ContainerInfoWithHost {
info: ContainerInfo;
host: string;
isSocket: boolean | undefined;
}
const getSuggestionFromContainer = (
container: ContainerInfoWithHost,
homarrContainer: ContainerInfoWithHost | undefined,
): IntegrationSuggestion | null => {
const integrationKind = getSuggestionKind(container.info);
if (!integrationKind) return null;
return {
containerId: container.info.Id,
name: getIntegrationName(integrationKind),
kind: integrationKind,
internalUrl: getInternalUrl(container, homarrContainer),
externalUrl: getExternalUrl(container),
createdAt: container.info.Created ? new Date(container.info.Created * 1000) : undefined,
};
};
const getSuggestionKind = (container: ContainerInfo): IntegrationKind | null => {
const [image, _tag] = container.Image.split(":");
if (!image) return null;
return (
objectEntries(integrationDefs).find(
([, integration]) =>
"containerImages" in integration &&
integration.containerImages.some((containerImage) => containerImage.toLowerCase() === image.toLowerCase()),
)?.[0] ?? null
);
};
const calculatePortScore = (port: Port): number => {
let score = 0;
if (port.PublicPort) score += 10; // Public ports are more likely to be used
if (port.PrivatePort % 1000 === 80) score += 5; // Default http ports are most likely suffixed with 80 like 8080 or 80
if (port.PrivatePort % 1000 === 443) score += 10; // Default https ports are most likely suffixed with 443 like 8443 or 443
return score;
};
const getPort = (container: ContainerInfoWithHost): Port | null => {
const ports = container.info.Ports.filter((port) => port.Type === "tcp");
return (
ports
.map((port) => ({ port, score: calculatePortScore(port) }))
.sort((portA, portB) => portB.score - portA.score)
.at(0)?.port ?? null
);
};
const getInternalUrl = (
container: ContainerInfoWithHost,
homarrContainer: ContainerInfoWithHost | undefined,
): string | null => {
const port = getPort(container);
if (!port) return null;
const protocol = port.PrivatePort % 1000 === 443 ? "https" : "http";
if (homarrContainer) {
const matchingNetwork = Object.entries(homarrContainer.info.NetworkSettings.Networks).find(
([_key, homarrNetwork]) =>
Object.entries(container.info.NetworkSettings.Networks).some(
([_name, network]) => homarrNetwork.NetworkID === network.NetworkID,
),
)?.[1];
if (matchingNetwork) return `${protocol}://${matchingNetwork.IPAddress}:${port.PrivatePort}`;
}
// TODO: Let user specify the internal ip-address for integrations --> use as default before falling back to host.docker.internal
if (container.host === homarrContainer?.host && container.isSocket === homarrContainer.isSocket) {
return `${protocol}://host.docker.internal:${port.PublicPort}`;
}
// TODO: Change this
return `${protocol}://localhost:${port.PublicPort}`;
};
const getExternalUrl = (container: ContainerInfoWithHost): string | null => {
const port = getPort(container);
if (!port) return null;
const protocol = port.PrivatePort % 1000 === 443 ? "https" : "http";
// TODO: Use some labels like "traefik.host" to determine the external url
// For now we just use localhost as external url
return `${protocol}://localhost:${port.PublicPort}`;
};
interface UpdateSecretInput {
integrationId: string;
value: string;
@@ -2,9 +2,10 @@ import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { asc, createId, eq, like } from "@homarr/db";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import { getServerSettingByKeyAsync, updateServerSettingByKeyAsync } from "@homarr/db/queries";
import { searchEngines, users } from "@homarr/db/schema";
import { createIntegrationAsync } from "@homarr/integrations";
import { logger } from "@homarr/log";
import { byIdSchema, paginatedSchema, searchSchema } from "@homarr/validation/common";
import { searchEngineEditSchema, searchEngineManageSchema } from "@homarr/validation/search-engine";
import { mediaRequestOptionsSchema, mediaRequestRequestSchema } from "@homarr/validation/widgets/media-request";
@@ -96,23 +97,35 @@ export const searchEngineRouter = createTRPCRouter({
});
}
const serverDefaultId = await getServerSettingByKeyAsync(ctx.db, "search").then(
(setting) => setting.defaultSearchEngineId,
);
const searchSettings = await getServerSettingByKeyAsync(ctx.db, "search");
if (serverDefaultId) {
return await ctx.db.query.searchEngines.findFirst({
where: eq(searchEngines.id, serverDefaultId),
with: {
integration: {
columns: {
kind: true,
url: true,
id: true,
},
if (!searchSettings.defaultSearchEngineId) return null;
const serverDefault = await ctx.db.query.searchEngines.findFirst({
where: eq(searchEngines.id, searchSettings.defaultSearchEngineId),
with: {
integration: {
columns: {
kind: true,
url: true,
id: true,
},
},
},
});
if (serverDefault) return serverDefault;
// Remove the default search engine ID from settings if it does not longer exist
try {
await updateServerSettingByKeyAsync(ctx.db, "search", {
...searchSettings,
defaultSearchEngineId: null,
});
} catch (error) {
logger.warn(
new Error("Failed to update search settings after default search engine not found", { cause: error }),
);
}
return null;
+8 -4
View File
@@ -19,10 +19,11 @@ const createDownloadClientIntegrationMiddleware = (action: IntegrationAction) =>
export const downloadsRouter = createTRPCRouter({
getJobsAndStatuses: publicProcedure
.concat(createDownloadClientIntegrationMiddleware("query"))
.query(async ({ ctx }) => {
.input(z.object({ limitPerIntegration: z.number().default(50) }))
.query(async ({ ctx, input }) => {
return await Promise.all(
ctx.integrations.map(async (integration) => {
const innerHandler = downloadClientRequestHandler.handler(integration, {});
const innerHandler = downloadClientRequestHandler.handler(integration, { limit: input.limitPerIntegration });
const { data, timestamp } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
@@ -40,7 +41,8 @@ export const downloadsRouter = createTRPCRouter({
}),
subscribeToJobsAndStatuses: publicProcedure
.concat(createDownloadClientIntegrationMiddleware("query"))
.subscription(({ ctx }) => {
.input(z.object({ limitPerIntegration: z.number().default(50) }))
.subscription(({ ctx, input }) => {
return observable<{
integration: Modify<Integration, { kind: IntegrationKindByCategory<"downloadClient"> }>;
data: DownloadClientJobsAndStatus;
@@ -48,7 +50,9 @@ export const downloadsRouter = createTRPCRouter({
const unsubscribes: (() => void)[] = [];
for (const integrationWithSecrets of ctx.integrations) {
const { decryptedSecrets: _, ...integration } = integrationWithSecrets;
const innerHandler = downloadClientRequestHandler.handler(integrationWithSecrets, {});
const innerHandler = downloadClientRequestHandler.handler(integrationWithSecrets, {
limit: input.limitPerIntegration,
});
const unsubscribe = innerHandler.subscribe((data) => {
emit.next({
integration,
+5 -5
View File
@@ -34,20 +34,20 @@
"@homarr/validation": "workspace:^0.1.0",
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.0.0",
"next": "15.3.2",
"ldapts": "8.0.1",
"next": "15.3.3",
"next-auth": "5.0.0-beta.28",
"react": "19.1.0",
"react-dom": "19.1.0",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/bcrypt": "5.0.2",
"@types/cookies": "0.9.0",
"eslint": "^9.27.0",
"@types/cookies": "0.9.1",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3"
}
@@ -23,21 +23,22 @@ export type BoardPermissionsProps = (
export const constructBoardPermissions = (board: BoardPermissionsProps, session: Session | null) => {
const creatorId = "creator" in board ? board.creator?.id : board.creatorId;
const isCreator = session !== null && session.user.id === creatorId;
return {
hasFullAccess:
session?.user.id === creatorId ||
isCreator ||
board.userPermissions.some(({ permission }) => permission === "full") ||
board.groupPermissions.some(({ permission }) => permission === "full") ||
(session?.user.permissions.includes("board-full-all") ?? false),
hasChangeAccess:
session?.user.id === creatorId ||
isCreator ||
board.userPermissions.some(({ permission }) => permission === "modify" || permission === "full") ||
board.groupPermissions.some(({ permission }) => permission === "modify" || permission === "full") ||
(session?.user.permissions.includes("board-modify-all") ?? false) ||
(session?.user.permissions.includes("board-full-all") ?? false),
hasViewAccess:
session?.user.id === creatorId ||
isCreator ||
board.userPermissions.length >= 1 ||
board.groupPermissions.length >= 1 ||
board.isPublic ||
@@ -286,4 +286,22 @@ describe("constructBoardPermissions", () => {
expect(result.hasChangeAccess).toBe(false);
expect(result.hasViewAccess).toBe(true);
});
test("should return all false when creator is null and session is null", () => {
// Arrange
const board = {
creator: null,
userPermissions: [],
groupPermissions: [],
isPublic: false,
};
const session = null;
// Act
const result = constructBoardPermissions(board, session);
// Assert
expect(result.hasFullAccess).toBe(false);
expect(result.hasChangeAccess).toBe(false);
expect(result.hasViewAccess).toBe(false);
});
});
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -30,7 +30,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+2 -2
View File
@@ -34,8 +34,8 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"esbuild": "^0.25.4",
"eslint": "^9.27.0",
"esbuild": "^0.25.5",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+3 -3
View File
@@ -30,18 +30,18 @@
"@homarr/env": "workspace:^0.1.0",
"@homarr/log": "workspace:^0.1.0",
"dayjs": "^1.11.13",
"next": "15.3.2",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"undici": "7.10.0",
"zod": "^3.25.23",
"zod": "^3.25.62",
"zod-validation-error": "^3.4.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ export class LoggingAgent extends Agent {
url.searchParams.set(key, "REDACTED");
});
logger.info(
logger.debug(
`Dispatching request ${url.toString().replaceAll("=&", "&")} (${Object.keys(options.headers ?? {}).length} headers)`,
);
return super.dispatch(options, handler);
+4 -4
View File
@@ -21,7 +21,7 @@ const REDACTED = "REDACTED";
describe("LoggingAgent should log all requests", () => {
test("should log all requests", () => {
// Arrange
const infoLogSpy = vi.spyOn(logger, "info");
const infoLogSpy = vi.spyOn(logger, "debug");
const agent = new LoggingAgent();
// Act
@@ -33,7 +33,7 @@ describe("LoggingAgent should log all requests", () => {
test("should show amount of headers", () => {
// Arrange
const infoLogSpy = vi.spyOn(logger, "info");
const infoLogSpy = vi.spyOn(logger, "debug");
const agent = new LoggingAgent();
// Act
@@ -68,7 +68,7 @@ describe("LoggingAgent should log all requests", () => {
[`/?stringWith13Chars=${"a".repeat(13)}`, `/?stringWith13Chars=${REDACTED}`],
])("should redact sensitive data in url https://homarr.dev%s", (path, expected) => {
// Arrange
const infoLogSpy = vi.spyOn(logger, "info");
const infoLogSpy = vi.spyOn(logger, "debug");
const agent = new LoggingAgent();
// Act
@@ -87,7 +87,7 @@ describe("LoggingAgent should log all requests", () => {
["date times", "/?datetime=2022-01-01T00:00:00.000Z"],
])("should not redact values that are %s", (_reason, path) => {
// Arrange
const infoLogSpy = vi.spyOn(logger, "info");
const infoLogSpy = vi.spyOn(logger, "debug");
const agent = new LoggingAgent();
// Act
+1 -1
View File
@@ -33,7 +33,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -7,6 +7,6 @@ import { cronJobRunnerChannel } from ".";
*/
export const registerCronJobRunner = () => {
cronJobRunnerChannel.subscribe((jobName) => {
jobGroup.runManually(jobName);
void jobGroup.runManuallyAsync(jobName);
});
};
+1 -1
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.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+2 -2
View File
@@ -25,14 +25,14 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"node-cron": "^3.0.3"
"node-cron": "^4.1.0"
},
"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.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+7 -7
View File
@@ -1,5 +1,6 @@
import { AxiosError } from "axios";
import cron from "node-cron";
import type { ScheduledTask } from "node-cron";
import { schedule, validate } from "node-cron";
import { Stopwatch } from "@homarr/common";
import type { MaybePromise } from "@homarr/common/types";
@@ -27,7 +28,7 @@ const createCallback = <TAllowedNames extends string, TName extends TAllowedName
options: CreateCronJobOptions,
creatorOptions: CreateCronJobCreatorOptions<TAllowedNames>,
) => {
const expectedMaximumDurationInMillis = options.expectedMaximumDurationInMillis ?? 1000;
const expectedMaximumDurationInMillis = options.expectedMaximumDurationInMillis ?? 2500;
return (callback: () => MaybePromise<void>) => {
const catchingCallbackAsync = async () => {
try {
@@ -64,12 +65,11 @@ const createCallback = <TAllowedNames extends string, TName extends TAllowedName
/**
* We are not using the runOnInit method as we want to run the job only once we start the cron job schedule manually.
* This allows us to always run it once we start it. Additionally it will not run the callback if only the cron job file is imported.
* This allows us to always run it once we start it. Additionally, it will not run the callback if only the cron job file is imported.
*/
let scheduledTask: cron.ScheduledTask | null = null;
let scheduledTask: ScheduledTask | null = null;
if (cronExpression !== "never") {
scheduledTask = cron.schedule(cronExpression, () => void catchingCallbackAsync(), {
scheduled: false,
scheduledTask = schedule(cronExpression, () => void catchingCallbackAsync(), {
name,
timezone: creatorOptions.timezone,
});
@@ -110,7 +110,7 @@ export const createCronJobCreator = <TAllowedNames extends string = string>(
options: CreateCronJobOptions = { runOnStart: false },
) => {
creatorOptions.logger.logDebug(`Validating cron expression '${cronExpression}' for job: ${name}`);
if (cronExpression !== "never" && !cron.validate(cronExpression)) {
if (cronExpression !== "never" && !validate(cronExpression)) {
throw new Error(`Invalid cron expression '${cronExpression}' for job '${name}'`);
}
creatorOptions.logger.logDebug(`Cron job expression '${cronExpression}' for job ${name} is valid`);
+8 -8
View File
@@ -34,33 +34,33 @@ export const createJobGroupCreator = <TAllowedNames extends string = string>(
options.logger.logInfo(`Starting schedule cron job ${job.name}.`);
await job.onStartAsync();
job.scheduledTask?.start();
await job.scheduledTask?.start();
},
startAllAsync: async () => {
for (const job of jobRegistry.values()) {
options.logger.logInfo(`Starting schedule of cron job ${job.name}.`);
await job.onStartAsync();
job.scheduledTask?.start();
await job.scheduledTask?.start();
}
},
runManually: (name: keyof TJobs) => {
runManuallyAsync: async (name: keyof TJobs) => {
const job = jobRegistry.get(name as string);
if (!job) return;
options.logger.logInfo(`Running schedule cron job ${job.name} manually.`);
job.scheduledTask?.now();
await job.scheduledTask?.execute();
},
stop: (name: keyof TJobs) => {
stopAsync: async (name: keyof TJobs) => {
const job = jobRegistry.get(name as string);
if (!job) return;
options.logger.logInfo(`Stopping schedule cron job ${job.name}.`);
job.scheduledTask?.stop();
await job.scheduledTask?.stop();
},
stopAll: () => {
stopAllAsync: async () => {
for (const job of jobRegistry.values()) {
options.logger.logInfo(`Stopping schedule cron job ${job.name}.`);
job.scheduledTask?.stop();
await job.scheduledTask?.stop();
}
},
getJobRegistry() {
+1 -1
View File
@@ -44,7 +44,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
@@ -1,10 +1,10 @@
import { EVERY_5_SECONDS } from "@homarr/cron-jobs-core/expressions";
import { EVERY_MINUTE } from "@homarr/cron-jobs-core/expressions";
import { dnsHoleRequestHandler } from "@homarr/request-handler/dns-hole";
import { createRequestIntegrationJobHandler } from "@homarr/request-handler/lib/cached-request-integration-job-handler";
import { createCronJob } from "../../lib";
export const dnsHoleJob = createCronJob("dnsHole", EVERY_5_SECONDS).withCallback(
export const dnsHoleJob = createCronJob("dnsHole", EVERY_MINUTE).withCallback(
createRequestIntegrationJobHandler(dnsHoleRequestHandler.handler, {
widgetKinds: ["dnsHoleSummary", "dnsHoleControls"],
getInput: {
@@ -8,7 +8,9 @@ export const downloadsJob = createCronJob("downloads", EVERY_5_SECONDS).withCall
createRequestIntegrationJobHandler(downloadClientRequestHandler.handler, {
widgetKinds: ["downloads"],
getInput: {
downloads: () => ({}),
downloads: (options) => ({
limit: options.limitPerIntegration,
}),
},
}),
);
+2 -2
View File
@@ -1,6 +1,6 @@
import SuperJSON from "superjson";
import { EVERY_5_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { EVERY_10_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { items } from "@homarr/db/schema";
import { logger } from "@homarr/log";
@@ -10,7 +10,7 @@ import { rssFeedsRequestHandler } from "@homarr/request-handler/rss-feeds";
import type { WidgetComponentProps } from "../../../widgets";
import { createCronJob } from "../lib";
export const rssFeedsJob = createCronJob("rssFeeds", EVERY_5_MINUTES).withCallback(async () => {
export const rssFeedsJob = createCronJob("rssFeeds", EVERY_10_MINUTES).withCallback(async () => {
const rssItems = await db.query.items.findMany({
where: eq(items.kind, "rssFeed"),
});
+6 -6
View File
@@ -44,13 +44,13 @@
"@homarr/env": "workspace:^0.1.0",
"@homarr/log": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/core": "^8.0.1",
"@mantine/core": "^8.1.0",
"@paralleldrive/cuid2": "^2.2.2",
"@testcontainers/mysql": "^10.28.0",
"@testcontainers/mysql": "^11.0.3",
"better-sqlite3": "^11.10.0",
"dotenv": "^16.5.0",
"drizzle-kit": "^0.31.1",
"drizzle-orm": "^0.43.1",
"drizzle-orm": "^0.44.2",
"drizzle-zod": "^0.7.1",
"mysql2": "3.14.1"
},
@@ -60,10 +60,10 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/better-sqlite3": "7.6.13",
"dotenv-cli": "^8.0.0",
"esbuild": "^0.25.4",
"eslint": "^9.27.0",
"esbuild": "^0.25.5",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"tsx": "4.19.4",
"tsx": "4.20.1",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ import * as mysqlSchema from "../schema/mysql";
describe("Mysql Migration", () => {
test("should add all tables and keys specified in migration files", async () => {
const mysqlContainer = await new MySqlContainer().start();
const mysqlContainer = await new MySqlContainer("mysql:latest").start();
const connection = mysql.createConnection({
host: mysqlContainer.getHost(),
+4 -4
View File
@@ -24,15 +24,15 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"fast-xml-parser": "^5.2.3",
"zod": "^3.25.23"
"fast-xml-parser": "^5.2.5",
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"tsx": "4.19.4",
"eslint": "^9.28.0",
"tsx": "4.20.1",
"typescript": "^5.8.3"
}
}
+94
View File
@@ -16,6 +16,7 @@ interface integrationDefinition {
iconUrl: string;
secretKinds: AtLeastOneOf<IntegrationSecretKind[]>; // at least one secret kind set is required
category: AtLeastOneOf<IntegrationCategory>;
containerImages?: string[]; // Optional, used for container images to suggest in the UI
}
export const integrationDefs = {
@@ -24,30 +25,45 @@ export const integrationDefs = {
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sabnzbd.svg",
category: ["downloadClient", "usenet"],
containerImages: ["linuxserver/sabnzbd", "lscr.io/linuxserver/sabnzbd", "ghcr.io/linuxserver/sabnzbd"],
},
nzbGet: {
name: "NZBGet",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nzbget.svg",
category: ["downloadClient", "usenet"],
containerImages: [
"nzbgetcom/nzbget",
"ghcr.io/nzbgetcom/nzbget",
"linuxserver/nzbget",
"lscr.io/linuxserver/nzbget",
"ghcr.io/linuxserver/nzbget",
],
},
deluge: {
name: "Deluge",
secretKinds: [["password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/deluge.svg",
category: ["downloadClient", "torrent"],
containerImages: ["linuxserver/deluge", "lscr.io/linuxserver/deluge", "ghcr.io/linuxserver/deluge"],
},
transmission: {
name: "Transmission",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/transmission.svg",
category: ["downloadClient", "torrent"],
containerImages: [
"linuxserver/transmission",
"lscr.io/linuxserver/transmission",
"ghcr.io/linuxserver/transmission",
],
},
qBittorrent: {
name: "qBittorrent",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/qbittorrent.svg",
category: ["downloadClient", "torrent"],
containerImages: ["linuxserver/qbittorrent", "lscr.io/linuxserver/qbittorrent", "ghcr.io/linuxserver/qbittorrent"],
},
aria2: {
name: "Aria2",
@@ -60,78 +76,144 @@ export const integrationDefs = {
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sonarr.svg",
category: ["calendar"],
containerImages: [
"hotio/sonarr",
"ghcr.io/hotio/sonarr",
"linuxserver/sonarr",
"lscr.io/linuxserver/sonarr",
"ghcr.io/linuxserver/sonarr",
],
},
radarr: {
name: "Radarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/radarr.svg",
category: ["calendar"],
containerImages: [
"hotio/radarr",
"ghcr.io/hotio/radarr",
"linuxserver/radarr",
"lscr.io/linuxserver/radarr",
"ghcr.io/linuxserver/radarr",
],
},
lidarr: {
name: "Lidarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/lidarr.svg",
category: ["calendar"],
containerImages: [
"hotio/lidarr",
"ghcr.io/hotio/lidarr",
"linuxserver/lidarr",
"lscr.io/linuxserver/lidarr",
"ghcr.io/linuxserver/lidarr",
],
},
readarr: {
name: "Readarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/readarr.svg",
category: ["calendar"],
containerImages: [
"hotio/readarr",
"ghcr.io/hotio/readarr",
"linuxserver/readarr",
"lscr.io/linuxserver/readarr",
"ghcr.io/linuxserver/readarr",
],
},
prowlarr: {
name: "Prowlarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/prowlarr.svg",
category: ["indexerManager"],
containerImages: [
"hotio/prowlarr",
"ghcr.io/hotio/prowlarr",
"linuxserver/prowlarr",
"lscr.io/linuxserver/prowlarr",
"ghcr.io/linuxserver/prowlarr",
],
},
jellyfin: {
name: "Jellyfin",
secretKinds: [["username", "password"], ["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyfin.svg",
category: ["mediaService"],
containerImages: [
"jellyfin/jellyfin",
"ghcr.io/jellyfin/jellyfin",
"linuxserver/jellyfin",
"lscr.io/linuxserver/jellyfin",
"ghcr.io/linuxserver/jellyfin",
],
},
emby: {
name: "Emby",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/emby.svg",
category: ["mediaService"],
containerImages: [
"emby/embyserver",
"emby/embyserver_arm64v8",
"emby/embyserver_arm32v7",
"linuxserver/emby",
"lscr.io/linuxserver/emby",
"ghcr.io/linuxserver/emby",
],
},
plex: {
name: "Plex",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/plex.svg",
category: ["mediaService"],
containerImages: ["plexinc/pms-docker", "linuxserver/plex", "lscr.io/linuxserver/plex", "ghcr.io/linuxserver/plex"],
},
jellyseerr: {
name: "Jellyseerr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyseerr.svg",
category: ["mediaSearch", "mediaRequest", "search"],
containerImages: ["fallenbagel/jellyseerr", "ghcr.io/fallenbagel/jellyseerr"],
},
overseerr: {
name: "Overseerr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/overseerr.svg",
category: ["mediaSearch", "mediaRequest", "search"],
containerImages: [
"sct/overseerr",
"linuxserver/overseerr",
"lscr.io/linuxserver/overseerr",
"ghcr.io/linuxserver/overseerr",
],
},
piHole: {
name: "Pi-hole",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/pi-hole.svg",
category: ["dnsHole"],
containerImages: ["pihole/pihole"],
},
adGuardHome: {
name: "AdGuard Home",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/adguard-home.svg",
category: ["dnsHole"],
containerImages: ["adguard/adguardhome"],
},
homeAssistant: {
name: "Home Assistant",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/home-assistant.svg",
category: ["smartHomeServer"],
containerImages: [
"homeassistant/home-assistant",
"linuxserver/home-assistant",
"lscr.io/linuxserver/home-assistant",
"ghcr.io/linuxserver/home-assistant",
],
},
openmediavault: {
name: "OpenMediaVault",
@@ -144,12 +226,14 @@ export const integrationDefs = {
secretKinds: [[]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/dashdot.png",
category: ["healthMonitoring"],
containerImages: ["mauricenino/dashdot", "ghcr.io/mauricenino/dashdot"],
},
tdarr: {
name: "Tdarr",
secretKinds: [[], ["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/tdarr.png",
category: ["mediaTranscoding"],
containerImages: ["haveagitgat/tdarr", "ghcr.io/haveagitgat/tdarr"],
},
proxmox: {
name: "Proxmox",
@@ -168,6 +252,16 @@ export const integrationDefs = {
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png",
category: ["networkController"],
containerImages: [
"jacobalberty/unifi",
"ghcr.io/jacobalberty/unifi",
"linuxserver/unifi-controller",
"lscr.io/linuxserver/unifi-controller",
"ghcr.io/linuxserver/unifi-controller",
"linuxserver/unifi-network-application",
"lscr.io/linuxserver/unifi-network-application",
"ghcr.io/linuxserver/unifi-network-application",
],
},
} as const satisfies Record<string, integrationDefinition>;
+3 -3
View File
@@ -26,14 +26,14 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"@homarr/env": "workspace:^0.1.0",
"dockerode": "^4.0.6"
"dockerode": "^4.0.7"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/dockerode": "^3.3.39",
"eslint": "^9.27.0",
"@types/dockerode": "^3.3.40",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+2 -1
View File
@@ -5,6 +5,7 @@ import { env } from "./env";
export interface DockerInstance {
host: string;
instance: Docker;
isSocket?: boolean;
}
export class DockerSingleton {
@@ -14,7 +15,7 @@ export class DockerSingleton {
const hostVariable = env.DOCKER_HOSTNAMES;
const portVariable = env.DOCKER_PORTS;
if (hostVariable === undefined || portVariable === undefined) {
return [{ host: "socket", instance: new Docker() }];
return [{ host: "socket", instance: new Docker(), isSocket: true }];
}
const hostnames = hostVariable.split(",");
const ports = portVariable.split(",");
+3 -3
View File
@@ -23,14 +23,14 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@t3-oss/env-nextjs": "^0.13.4",
"zod": "^3.25.23"
"@t3-oss/env-nextjs": "^0.13.8",
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+3 -3
View File
@@ -26,14 +26,14 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^8.0.1",
"zod": "^3.25.23"
"@mantine/form": "^8.1.0",
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+3 -3
View File
@@ -29,15 +29,15 @@
"@homarr/notifications": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.0.1",
"@mantine/core": "^8.1.0",
"react": "19.1.0",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+1 -1
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.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+2 -2
View File
@@ -44,7 +44,7 @@
"tsdav": "^2.1.4",
"undici": "7.10.0",
"xml2js": "^0.6.2",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -52,7 +52,7 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node-unifi": "^2.5.1",
"@types/xml2js": "^0.4.14",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
@@ -12,7 +12,7 @@ import type { DownloadClientItem } from "../../interfaces/downloads/download-cli
import type { Aria2Download, Aria2GetClient } from "./aria2-types";
export class Aria2Integration extends DownloadClientIntegration {
public async getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus> {
public async getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus> {
const client = this.getClient();
const keys: (keyof Aria2Download)[] = [
"bittorrent",
@@ -27,12 +27,12 @@ export class Aria2Integration extends DownloadClientIntegration {
];
const [activeDownloads, waitingDownloads, stoppedDownloads, globalStats] = await Promise.all([
client.tellActive(),
client.tellWaiting(0, 1000, keys),
client.tellStopped(0, 1000, keys),
client.tellWaiting(0, input.limit, keys),
client.tellStopped(0, input.limit, keys),
client.getGlobalStat(),
]);
const downloads = [...activeDownloads, ...waitingDownloads, ...stoppedDownloads];
const downloads = [...activeDownloads, ...waitingDownloads, ...stoppedDownloads].slice(0, input.limit);
const allPaused = downloads.every((download) => download.status === "paused");
return {
@@ -29,9 +29,10 @@ export class DelugeIntegration extends DownloadClientIntegration {
};
}
public async getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus> {
public async getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus> {
const type = "torrent";
const client = await this.getClientAsync();
// Currently there is no way to limit the number of returned torrents
const {
stats: { download_rate, upload_rate },
torrents: rawTorrents,
@@ -49,27 +50,29 @@ export class DelugeIntegration extends DownloadClientIntegration {
},
types: [type],
};
const items = torrents.map((torrent): DownloadClientItem => {
const state = DelugeIntegration.getTorrentState(torrent.state);
return {
type,
id: torrent.id,
index: torrent.queue,
name: torrent.name,
size: torrent.total_wanted,
sent: torrent.total_uploaded,
downSpeed: torrent.progress !== 100 ? torrent.download_payload_rate : undefined,
upSpeed: torrent.upload_payload_rate,
time:
torrent.progress === 100
? Math.min((torrent.completed_time - dayjs().unix()) * 1000, -1)
: Math.max(torrent.eta * 1000, 0),
added: torrent.time_added * 1000,
state,
progress: torrent.progress / 100,
category: torrent.label,
};
});
const items = torrents
.map((torrent): DownloadClientItem => {
const state = DelugeIntegration.getTorrentState(torrent.state);
return {
type,
id: torrent.id,
index: torrent.queue,
name: torrent.name,
size: torrent.total_wanted,
sent: torrent.total_uploaded,
downSpeed: torrent.progress !== 100 ? torrent.download_payload_rate : undefined,
upSpeed: torrent.upload_payload_rate,
time:
torrent.progress === 100
? Math.min((torrent.completed_time - dayjs().unix()) * 1000, -1)
: Math.max(torrent.eta * 1000, 0),
added: torrent.time_added * 1000,
state,
progress: torrent.progress / 100,
category: torrent.label,
};
})
.slice(0, input.limit);
return { status, items };
}
@@ -20,7 +20,7 @@ export class NzbGetIntegration extends DownloadClientIntegration {
};
}
public async getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus> {
public async getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus> {
const type = "usenet";
const queue = await this.nzbGetApiCallAsync("listgroups");
const history = await this.nzbGetApiCallAsync("history");
@@ -65,7 +65,8 @@ export class NzbGetIntegration extends DownloadClientIntegration {
category: file.Category,
};
}),
);
)
.slice(0, input.limit);
return { status, items };
}
@@ -26,10 +26,10 @@ export class QBitTorrentIntegration extends DownloadClientIntegration {
};
}
public async getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus> {
public async getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus> {
const type = "torrent";
const client = await this.getClientAsync();
const torrents = await client.listTorrents();
const torrents = await client.listTorrents({ limit: input.limit });
const rates = torrents.reduce(
({ down, up }, { dlspeed, upspeed }) => ({ down: down + dlspeed, up: up + upspeed }),
{ down: 0, up: 0 },
@@ -22,10 +22,14 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
return { success: true };
}
public async getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus> {
public async getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus> {
const type = "usenet";
const { queue } = await queueSchema.parseAsync(await this.sabNzbApiCallAsync("queue"));
const { history } = await historySchema.parseAsync(await this.sabNzbApiCallAsync("history"));
const { queue } = await queueSchema.parseAsync(
await this.sabNzbApiCallAsync("queue", { limit: input.limit.toString() }),
);
const { history } = await historySchema.parseAsync(
await this.sabNzbApiCallAsync("history", { limit: input.limit.toString() }),
);
const status: DownloadClientStatus = {
paused: queue.paused,
rates: { down: Math.floor(Number(queue.kbpersec) * 1024) }, //Actually rounded kiBps ()
@@ -73,7 +77,8 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
category: slot.category,
};
}),
);
)
.slice(0, input.limit);
return { status, items };
}
@@ -23,9 +23,10 @@ export class TransmissionIntegration extends DownloadClientIntegration {
};
}
public async getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus> {
public async getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus> {
const type = "torrent";
const client = await this.getClientAsync();
// Currently there is no way to limit the number of returned torrents
const { torrents } = (await client.listTorrents()).arguments;
const rates = torrents.reduce(
({ down, up }, { rateDownload, rateUpload }) => ({ down: down + rateDownload, up: up + rateUpload }),
@@ -34,27 +35,29 @@ export class TransmissionIntegration extends DownloadClientIntegration {
const paused =
torrents.find(({ status }) => TransmissionIntegration.getTorrentState(status) !== "paused") === undefined;
const status: DownloadClientStatus = { paused, rates, types: [type] };
const items = torrents.map((torrent): DownloadClientItem => {
const state = TransmissionIntegration.getTorrentState(torrent.status);
return {
type,
id: torrent.hashString,
index: torrent.queuePosition,
name: torrent.name,
size: torrent.totalSize,
sent: torrent.uploadedEver,
downSpeed: torrent.percentDone !== 1 ? torrent.rateDownload : undefined,
upSpeed: torrent.rateUpload,
time:
torrent.percentDone === 1
? Math.min(torrent.doneDate * 1000 - dayjs().valueOf(), -1)
: Math.max(torrent.eta * 1000, 0),
added: torrent.addedDate * 1000,
state,
progress: torrent.percentDone,
category: torrent.labels,
};
});
const items = torrents
.map((torrent): DownloadClientItem => {
const state = TransmissionIntegration.getTorrentState(torrent.status);
return {
type,
id: torrent.hashString,
index: torrent.queuePosition,
name: torrent.name,
size: torrent.totalSize,
sent: torrent.uploadedEver,
downSpeed: torrent.percentDone !== 1 ? torrent.rateDownload : undefined,
upSpeed: torrent.rateUpload,
time:
torrent.percentDone === 1
? Math.min(torrent.doneDate * 1000 - dayjs().valueOf(), -1)
: Math.max(torrent.eta * 1000, 0),
added: torrent.addedDate * 1000,
state,
progress: torrent.percentDone,
category: torrent.labels,
};
})
.slice(0, input.limit);
return { status, items };
}
@@ -4,7 +4,7 @@ import type { DownloadClientItem } from "./download-client-items";
export abstract class DownloadClientIntegration extends Integration {
/** Get download client's status and list of all of it's items */
public abstract getClientJobsAndStatusAsync(): Promise<DownloadClientJobsAndStatus>;
public abstract getClientJobsAndStatusAsync(input: { limit: number }): Promise<DownloadClientJobsAndStatus>;
/** Pauses the client or all of it's items */
public abstract pauseQueueAsync(): Promise<void>;
/** Pause a single item using it's ID */
@@ -1,6 +1,3 @@
import dayjs from "dayjs";
import objectSupport from "dayjs/plugin/objectSupport";
import utc from "dayjs/plugin/utc";
import * as ical from "node-ical";
import { DAVClient } from "tsdav";
import type { Dispatcher, RequestInit as UndiciFetchRequestInit } from "undici";
@@ -15,9 +12,6 @@ import { Integration } from "../base/integration";
import type { TestingResult } from "../base/test-connection/test-connection-service";
import type { CalendarEvent } from "../calendar-types";
dayjs.extend(utc);
dayjs.extend(objectSupport);
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
export class NextcloudIntegration extends Integration {
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
@@ -27,7 +21,7 @@ export class NextcloudIntegration extends Integration {
return { success: true };
}
public async getCalendarEventsAsync(start: Date, end: Date): Promise<CalendarEvent[]> {
public async getCalendarEventsAsync(start: Date, end: Date, _showUnmonitored?: boolean): Promise<CalendarEvent[]> {
const client = await this.createCalendarClientAsync();
await client.login();
@@ -57,14 +51,7 @@ export class NextcloudIntegration extends Integration {
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
const date = dayjs.utc({
days: veventObject.start.getDay(),
month: veventObject.start.getMonth(),
year: veventObject.start.getFullYear(),
hours: veventObject.start.getHours(),
minutes: veventObject.start.getMinutes(),
seconds: veventObject.start.getSeconds(),
});
const date = veventObject.start;
const eventUrlWithoutHost = new URL(event.url).pathname;
const dateInMillis = veventObject.start.valueOf();
@@ -75,7 +62,7 @@ export class NextcloudIntegration extends Integration {
return {
name: veventObject.summary,
date: date.toDate(),
date,
subName: "",
description: veventObject.description,
links: [
@@ -66,7 +66,7 @@ export class OpenMediaVaultIntegration extends Integration {
return {
version: systemResult.data.response.version,
cpuModelName: systemResult.data.response.cpuModelName,
cpuModelName: systemResult.data.response.cpuModelName ?? "Unknown CPU",
cpuUtilization: systemResult.data.response.cpuUtilization,
memUsed: systemResult.data.response.memUsed,
memAvailable: systemResult.data.response.memAvailable,
@@ -4,7 +4,7 @@ import { z } from "zod";
export const systemInformationSchema = z.object({
response: z.object({
version: z.string(),
cpuModelName: z.string(),
cpuModelName: z.string().nullable(),
cpuUtilization: z.number(),
memUsed: z.string(),
memAvailable: z.string(),
@@ -77,6 +77,7 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
protected async testingAsync({ fetchAsync }: IntegrationTestingInput): Promise<TestingResult> {
const sessionId = await this.getSessionAsync(fetchAsync);
await this.clearSessionAsync(sessionId, fetchAsync);
await this.sessionStore.clearAsync();
return { success: true };
}
@@ -69,6 +69,7 @@ const mapResource = (resource: Proxmox.clusterResourcesResources): Resource | nu
const mapComputeResource = (resource: Proxmox.clusterResourcesResources): Omit<ComputeResourceBase<string>, "type"> => {
return {
id: resource.id,
cpu: {
utilization: resource.cpu ?? 0,
cores: resource.maxcpu ?? 0,
@@ -114,6 +115,7 @@ const mapVmResource = (resource: Proxmox.clusterResourcesResources): LxcResource
const mapStorageResource = (resource: Proxmox.clusterResourcesResources): StorageResource => {
return {
id: resource.id,
type: "storage",
name: resource.storage ?? "",
node: resource.node ?? "",
@@ -7,6 +7,7 @@ interface ResourceBase<TType extends string> {
}
export interface ComputeResourceBase<TType extends string> extends ResourceBase<TType> {
id: string;
cpu: {
utilization: number; // previously cpu (0-1)
cores: number; // previously cpuCores
@@ -40,6 +41,7 @@ export interface QemuResource extends ComputeResourceBase<"qemu"> {
}
export interface StorageResource extends ResourceBase<"storage"> {
id: string;
storagePlugin: string;
used: number; // previously disk
total: number; // previously maxDisk
+5 -5
View File
@@ -46,7 +46,7 @@ describe("Aria2 integration", () => {
// Acts
const actAsync = async () => await aria2Integration.pauseQueueAsync();
const getAsync = async () => await aria2Integration.getClientJobsAndStatusAsync();
const getAsync = async () => await aria2Integration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(actAsync()).resolves.not.toThrow();
@@ -62,7 +62,7 @@ describe("Aria2 integration", () => {
const aria2Integration = createAria2Intergration(startedContainer, API_KEY);
// Act
const getAsync = async () => await aria2Integration.getClientJobsAndStatusAsync();
const getAsync = async () => await aria2Integration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.not.toThrow();
@@ -81,7 +81,7 @@ describe("Aria2 integration", () => {
await aria2AddItemAsync(startedContainer, API_KEY, aria2Integration);
// Act
const getAsync = async () => await aria2Integration.getClientJobsAndStatusAsync();
const getAsync = async () => await aria2Integration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.not.toThrow();
@@ -104,7 +104,7 @@ describe("Aria2 integration", () => {
await expect(actAsync()).resolves.not.toThrow();
// NzbGet is slow and we wait for a second before querying the items. Test was flaky without this.
await new Promise((resolve) => setTimeout(resolve, 1000));
const result = await aria2Integration.getClientJobsAndStatusAsync();
const result = await aria2Integration.getClientJobsAndStatusAsync({ limit: 99 });
expect(result.items).toHaveLength(0);
// Cleanup
@@ -153,7 +153,7 @@ const aria2AddItemAsync = async (container: StartedTestContainer, apiKey: string
const {
items: [item],
} = await integration.getClientJobsAndStatusAsync();
} = await integration.getClientJobsAndStatusAsync({ limit: 99 });
if (!item) {
throw new Error("No item found");
@@ -64,26 +64,29 @@ describe("Home Assistant integration", () => {
const prepareHomeAssistantContainerAsync = async () => {
const homeAssistantContainer = createHomeAssistantContainer();
const startedContainer = await homeAssistantContainer.start();
await startedContainer.exec(["unzip", "-o", "/tmp/config.zip", "-d", "/config"]);
await startedContainer.restart();
return startedContainer;
};
const createHomeAssistantContainer = () => {
return new GenericContainer(IMAGE_NAME)
.withCopyFilesToContainer([
{
source: join(__dirname, "/volumes/home-assistant-config.zip"),
target: "/tmp/config.zip",
},
])
.withPrivilegedMode()
.withExposedPorts(8123)
.withWaitStrategy(Wait.forHttp("/", 8123));
return (
new GenericContainer(IMAGE_NAME)
.withCopyFilesToContainer([
{
source: join(__dirname, "/volumes/home-assistant-config.zip"),
target: "/tmp/config.zip",
},
])
.withPrivilegedMode()
.withExposedPorts(8123)
// This has to be a page that is not redirected (or a status code has to be defined withStatusCode(statusCode))
.withWaitStrategy(Wait.forHttp("/onboarding.html", 8123))
);
};
const createHomeAssistantIntegration = (container: StartedTestContainer, apiKeyOverride?: string) => {
console.log("Creating Home Assistant integration...");
return new HomeAssistantIntegration({
id: "1",
decryptedSecrets: [
+8 -8
View File
@@ -69,7 +69,7 @@ describe("Nzbget integration", () => {
// Acts
const actAsync = async () => await nzbGetIntegration.pauseQueueAsync();
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(actAsync()).resolves.not.toThrow();
@@ -87,7 +87,7 @@ describe("Nzbget integration", () => {
// Acts
const actAsync = async () => await nzbGetIntegration.resumeQueueAsync();
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(actAsync()).resolves.not.toThrow();
@@ -105,7 +105,7 @@ describe("Nzbget integration", () => {
const nzbGetIntegration = createNzbGetIntegration(startedContainer, username, password);
// Act
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.not.toThrow();
@@ -124,7 +124,7 @@ describe("Nzbget integration", () => {
await nzbGetAddItemAsync(startedContainer, username, password, nzbGetIntegration);
// Act
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.not.toThrow();
@@ -145,9 +145,9 @@ describe("Nzbget integration", () => {
// Assert
await expect(actAsync()).resolves.not.toThrow();
// NzbGet is slow and we wait for a second before querying the items. Test was flaky without this.
await new Promise((resolve) => setTimeout(resolve, 1000));
const result = await nzbGetIntegration.getClientJobsAndStatusAsync();
// NzbGet is slow and we wait for a few seconds before querying the items. Test was flaky without this.
await new Promise((resolve) => setTimeout(resolve, 5000));
const result = await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
expect(result.items).toHaveLength(0);
// Cleanup
@@ -209,7 +209,7 @@ const nzbGetAddItemAsync = async (
const {
items: [item],
} = await integration.getClientJobsAndStatusAsync();
} = await integration.getClientJobsAndStatusAsync({ limit: 99 });
if (!item) {
throw new Error("No item found");
+9 -6
View File
@@ -211,12 +211,15 @@ const createPiHoleIntegrationV5 = (container: StartedTestContainer, apiKey: stri
};
const createPiHoleV6Container = (password: string) => {
return new GenericContainer("pihole/pihole:latest")
.withEnvironment({
FTLCONF_webserver_api_password: password,
})
.withExposedPorts(80)
.withWaitStrategy(Wait.forHttp("/admin", 80));
return (
new GenericContainer("pihole/pihole:latest")
.withEnvironment({
FTLCONF_webserver_api_password: password,
})
.withExposedPorts(80)
// This has to be a page that is not redirected (or a status code has to be defined withStatusCode(statusCode))
.withWaitStrategy(Wait.forHttp("/admin/login", 80))
);
};
const createPiHoleIntegrationV6 = (container: StartedTestContainer, apiKey: string) => {
+21 -18
View File
@@ -67,7 +67,7 @@ describe("Sabnzbd integration", () => {
// Acts
const actAsync = async () => await sabnzbdIntegration.pauseQueueAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(actAsync()).resolves.not.toThrow();
@@ -85,7 +85,7 @@ describe("Sabnzbd integration", () => {
// Acts
const actAsync = async () => await sabnzbdIntegration.resumeQueueAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(actAsync()).resolves.not.toThrow();
@@ -103,7 +103,7 @@ describe("Sabnzbd integration", () => {
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
// Act
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.not.toThrow();
@@ -122,7 +122,7 @@ describe("Sabnzbd integration", () => {
await sabNzbdAddItemAsync(startedContainer, DEFAULT_API_KEY, sabnzbdIntegration);
// Act
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.not.toThrow();
@@ -140,7 +140,7 @@ describe("Sabnzbd integration", () => {
// Act
const actAsync = async () => await sabnzbdIntegration.pauseItemAsync(item);
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.toMatchObject({ items: [{ ...item, state: "downloading" }] });
@@ -160,7 +160,7 @@ describe("Sabnzbd integration", () => {
// Act
const actAsync = async () => await sabnzbdIntegration.resumeItemAsync(item);
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(getAsync()).resolves.toMatchObject({ items: [{ ...item, state: "paused" }] });
@@ -180,7 +180,7 @@ describe("Sabnzbd integration", () => {
// Act - fromDisk already doesn't work for sabnzbd, so only test deletion itself.
const actAsync = async () =>
await sabnzbdIntegration.deleteItemAsync({ ...item, progress: 0 } as DownloadClientItem, false);
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync({ limit: 99 });
// Assert
await expect(actAsync()).resolves.not.toThrow();
@@ -192,16 +192,19 @@ describe("Sabnzbd integration", () => {
});
const createSabnzbdContainer = () => {
return new GenericContainer(IMAGE_NAME)
.withCopyFilesToContainer([
{
source: join(__dirname, "/volumes/usenet/sabnzbd.ini"),
target: "/config/sabnzbd.ini",
},
])
.withExposedPorts(1212)
.withEnvironment({ PUID: "0", PGID: "0" })
.withWaitStrategy(Wait.forHttp("/", 1212));
return (
new GenericContainer(IMAGE_NAME)
.withCopyFilesToContainer([
{
source: join(__dirname, "/volumes/usenet/sabnzbd.ini"),
target: "/config/sabnzbd.ini",
},
])
.withExposedPorts(1212)
.withEnvironment({ PUID: "0", PGID: "0" })
// This has to be a page that is not redirected (or a status code has to be defined withStatusCode(statusCode))
.withWaitStrategy(Wait.forHttp("/sabnzbd/wizard/", 1212))
);
};
const createSabnzbdIntegration = (container: StartedTestContainer, apiKey: string) => {
@@ -242,7 +245,7 @@ const sabNzbdAddItemAsync = async (
for (let i = 0; i < 5; i++) {
const {
items: [item],
} = await integration.getClientJobsAndStatusAsync();
} = await integration.getClientJobsAndStatusAsync({ limit: 99 });
if (item) return item;
}
// Throws if it can't find the item
+2 -2
View File
@@ -27,13 +27,13 @@
"ioredis": "5.6.1",
"superjson": "2.2.2",
"winston": "3.17.0",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+5 -5
View File
@@ -33,19 +33,19 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.0.1",
"@tabler/icons-react": "^3.33.0",
"@mantine/core": "^8.1.0",
"@tabler/icons-react": "^3.34.0",
"dayjs": "^1.11.13",
"next": "15.3.2",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
@@ -1,5 +1,4 @@
import { useMemo, useState } from "react";
import Image from "next/image";
import { Button, Card, Center, Grid, Input, Stack, Text } from "@mantine/core";
import { IconPlus, IconSearch } from "@tabler/icons-react";
@@ -23,7 +22,7 @@ export const AppSelectModal = createModal<AppSelectModalProps>(({ actions, inner
() =>
apps
.filter((app) => app.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => a.name.localeCompare(b.name)),
.sort((appA, appB) => appA.name.localeCompare(appB.name)),
[apps, search],
);
@@ -88,7 +87,7 @@ export const AppSelectModal = createModal<AppSelectModalProps>(({ actions, inner
<Stack justify="space-between" h="100%">
<Stack gap="xs">
<Center>
<Image src={app.iconUrl || ""} alt={app.name} width={24} height={24} />
<img src={app.iconUrl} alt={app.name} width={24} height={24} />
</Center>
<Text lh={1.2} style={{ whiteSpace: "normal" }} ta="center">
{app.name}
+3 -3
View File
@@ -24,15 +24,15 @@
"dependencies": {
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.0.1",
"@mantine/hooks": "^8.0.1",
"@mantine/core": "^8.1.0",
"@mantine/hooks": "^8.1.0",
"react": "19.1.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+3 -3
View File
@@ -24,14 +24,14 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/ui": "workspace:^0.1.0",
"@mantine/notifications": "^8.0.1",
"@tabler/icons-react": "^3.33.0"
"@mantine/notifications": "^8.1.0",
"@tabler/icons-react": "^3.34.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+5 -5
View File
@@ -37,14 +37,14 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.0.1",
"@mantine/hooks": "^8.0.1",
"@mantine/core": "^8.1.0",
"@mantine/hooks": "^8.1.0",
"adm-zip": "0.5.16",
"next": "15.3.2",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"superjson": "2.2.2",
"zod": "^3.25.23",
"zod": "^3.25.62",
"zod-form-data": "^2.0.7"
},
"devDependencies": {
@@ -52,7 +52,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/adm-zip": "0.5.7",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
@@ -77,6 +77,7 @@ const optionMapping: OptionMapping = {
descendingDefaultSort: () => false,
showCompletedUsenet: () => true,
showCompletedHttp: () => true,
limitPerIntegration: () => undefined,
},
weather: {
forecastDayCount: (oldOptions) => oldOptions.forecastDays,
@@ -156,6 +157,21 @@ const optionMapping: OptionMapping = {
defaultTab: (oldOptions) => ("defaultTabState" in oldOptions ? oldOptions.defaultTabState : undefined),
sectionIndicatorRequirement: (oldOptions) =>
"sectionIndicatorColor" in oldOptions ? oldOptions.sectionIndicatorColor : undefined,
showUptime: () => undefined,
visibleClusterSections: (oldOptions) => {
if (!("showNode" in oldOptions)) return undefined;
const oldKeys = {
showNode: "node" as const,
showLXCs: "lxc" as const,
showVM: "qemu" as const,
showStorage: "storage" as const,
} satisfies Partial<Record<keyof typeof oldOptions, string>>;
return objectEntries(oldKeys)
.filter(([key]) => oldOptions[key])
.map(([_, section]) => section);
},
},
mediaTranscoding: {
defaultView: (oldOptions) => oldOptions.defaultView,
+2 -2
View File
@@ -23,13 +23,13 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"zod": "^3.25.23"
"zod": "^3.25.62"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -30,7 +30,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -33,7 +33,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+2 -2
View File
@@ -30,14 +30,14 @@
"@homarr/log": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"dayjs": "^1.11.13",
"octokit": "^5.0.2",
"octokit": "^5.0.3",
"superjson": "2.2.2"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+4 -2
View File
@@ -13,8 +13,10 @@ export const calendarMonthRequestHandler = createCachedIntegrationRequestHandler
>({
async requestAsync(integration, input) {
const integrationInstance = await createIntegrationAsync(integration);
const startDate = dayjs().year(input.year).month(input.month).startOf("month");
const endDate = startDate.clone().endOf("month");
// Calendar component shows up to 6 days before and after the month, for example if 1. of january is sunday, it shows the last 6 days of december.
const startDate = dayjs().year(input.year).month(input.month).startOf("month").subtract(6, "days");
const endDate = dayjs().year(input.year).month(input.month).endOf("month").add(6, "days");
return await integrationInstance.getCalendarEventsAsync(
startDate.toDate(),
endDate.toDate(),
+3 -3
View File
@@ -9,11 +9,11 @@ import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-
export const downloadClientRequestHandler = createCachedIntegrationRequestHandler<
DownloadClientJobsAndStatus,
IntegrationKindByCategory<"downloadClient">,
Record<string, never>
{ limit: number }
>({
async requestAsync(integration, _input) {
async requestAsync(integration, input) {
const integrationInstance = await createIntegrationAsync(integration);
return await integrationInstance.getClientJobsAndStatusAsync();
return await integrationInstance.getClientJobsAndStatusAsync(input);
},
cacheDuration: dayjs.duration(5, "seconds"),
queryKey: "downloadClientJobStatus",
+1 -1
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.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+3 -3
View File
@@ -26,8 +26,8 @@
"@homarr/api": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^8.0.1",
"next": "15.3.2",
"@mantine/dates": "^8.1.0",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0"
},
@@ -35,7 +35,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+7 -7
View File
@@ -33,12 +33,12 @@
"@homarr/settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.0.1",
"@mantine/hooks": "^8.0.1",
"@mantine/spotlight": "^8.0.1",
"@tabler/icons-react": "^3.33.0",
"jotai": "^2.12.4",
"next": "15.3.2",
"@mantine/core": "^8.1.0",
"@mantine/hooks": "^8.1.0",
"@mantine/spotlight": "^8.1.0",
"@tabler/icons-react": "^3.34.0",
"jotai": "^2.12.5",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"use-deep-compare-effect": "^1.8.1"
@@ -47,7 +47,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+2 -2
View File
@@ -32,7 +32,7 @@
"dayjs": "^1.11.13",
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "15.3.2",
"next": "15.3.3",
"next-intl": "4.1.0",
"react": "19.1.0",
"react-dom": "19.1.0"
@@ -41,7 +41,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"typescript": "^5.8.3"
}
}
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": ""
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": ""
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": ""
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "",
"notification": {
+203 -160
View File
@@ -263,14 +263,14 @@
}
},
"removeImage": {
"label": "删除图像",
"label": "删除头像",
"confirm": "确定要移除这张图片吗?",
"notification": {
"success": {
"message": "图像删除成功"
},
"error": {
"message": "无法删除图像"
"message": "无法删除头像"
}
}
}
@@ -612,17 +612,17 @@
"select": {
"label": "选择应用",
"notFound": "未找到应用",
"search": "",
"noResults": "",
"action": "",
"title": ""
"search": "搜索应用",
"noResults": "没有结果",
"action": "选择 {app}",
"title": "选择一个应用程序添加到此面板"
},
"create": {
"title": "",
"description": "",
"action": ""
"title": "创建应用",
"description": "创建应用 ",
"action": "打开应用创建"
},
"add": ""
"add": "添加应用"
}
},
"integration": {
@@ -705,125 +705,125 @@
"error": {
"common": {
"cause": {
"title": ""
"title": "更多详情"
}
},
"unknown": {
"title": "",
"description": ""
"title": "未知错误",
"description": "发生未知错误,打开下面的原因以查看更多详情"
},
"parse": {
"title": "",
"description": ""
"title": "解析错误",
"description": "无法解析该响应。请确认URL指向服务的基本URL。"
},
"authorization": {
"title": "",
"description": ""
"title": "授权错误",
"description": "请求未被授权。请验证凭据是否正确,并且您拥有足够的权限。"
},
"statusCode": {
"title": "",
"description": "",
"otherDescription": "",
"title": "响应错误",
"description": "收到来自<url></url>的意外{statusCode} ({reason})响应。请验证URL是否指向集成的基本URL。",
"otherDescription": "从<url></url>收到意外的{statusCode}响应。请验证URL是否指向集成的基本URL。",
"reason": {
"badRequest": "",
"notFound": "",
"tooManyRequests": "",
"internalServerError": "",
"serviceUnavailable": "",
"gatewayTimeout": ""
"badRequest": "错误请求",
"notFound": "未找到",
"tooManyRequests": "请求过于频繁",
"internalServerError": "服务器内部错误",
"serviceUnavailable": "服务暂时不可用",
"gatewayTimeout": "网关超时"
}
},
"certificate": {
"title": "",
"title": "证书错误",
"description": {
"expired": "",
"notYetValid": "",
"untrusted": "",
"hostnameMismatch": ""
"expired": "此证书已经过期。",
"notYetValid": "此证书尚未生效。",
"untrusted": "此证书不受信任。",
"hostnameMismatch": "证书主机名与 URL 不匹配。"
},
"alert": {
"permission": {
"title": "",
"message": ""
"title": "权限不足",
"message": "您无权信任或上传证书。请与您的管理员联系上传必要的根证书。"
},
"hostnameMismatch": {
"title": "",
"message": ""
"title": "主机名不匹配",
"message": "证书中的主机名与您连接的主机名不匹配。 这可能会显示安全风险,但您仍然可以选择信任此证书。"
},
"extract": {
"title": "",
"message": ""
"title": "CA证书提取失败",
"message": "只有自签署证书链可以自动获取。如果使用的是自签名证书,请确保手动上传CA证书。您可以找到有关如何执行此操作的说明<docsLink></docsLink>。"
}
},
"action": {
"retry": {
"label": ""
"label": "重试创建"
},
"trust": {
"label": ""
"label": "信任证书"
},
"upload": {
"label": ""
"label": "上传证书"
}
},
"hostnameMismatch": {
"confirm": {
"title": "",
"message": ""
"title": "信任主机名不匹配",
"message": "您确定要信任主机名不匹配的证书吗?"
},
"notification": {
"success": {
"title": "",
"message": ""
"title": "可信证书",
"message": "将主机名添加到受信任证书列表中"
},
"error": {
"title": "",
"message": ""
"title": "信任证书失败",
"message": "主机名不匹配的证书不可信"
}
}
},
"selfSigned": {
"confirm": {
"title": "",
"message": ""
"title": "信任自签名证书",
"message": "您确定要信任此自签名证书吗?"
},
"notification": {
"success": {
"title": "",
"message": ""
"title": "可信证书",
"message": "已添加证书到可信证书列表"
},
"error": {
"title": "",
"message": ""
"title": "信任证书失败",
"message": "向受信任证书列表添加证书失败"
}
}
},
"details": {
"title": "",
"description": "",
"title": "详情",
"description": "在决定信任证书信息之前,请检查该证书信息。",
"content": {
"action": "",
"title": ""
"action": "显示内容",
"title": "PEM 证书"
}
}
},
"request": {
"title": "",
"title": "错误请求",
"description": {
"connection": {
"hostUnreachable": "",
"networkUnreachable": "",
"refused": "",
"reset": ""
"hostUnreachable": "无法连接到服务器。这通常意味着主机离线或无法从您的网络访问。",
"networkUnreachable": "网络不可达。请检查网络连接或网络配置。",
"refused": "服务器拒绝连接。它可能没有运行或正在拒绝指定端口上的请求。",
"reset": "服务器意外关闭了连接。如果服务器不稳定或重新启动,就会发生这种情况。"
},
"dns": {
"notFound": "",
"timeout": "",
"noAnswer": ""
"notFound": "找不到服务器地址。请检查URL是否有拼写错误或无效的域名。",
"timeout": "DNS查找超时。这可能是暂时的问题,请稍后再试。",
"noAnswer": "DNS服务器没有返回有效的响应。域可能存在,但没有有效记录。"
},
"timeout": {
"aborted": "",
"timeout": ""
"aborted": "请求在完成之前被中止。这可能是由于用户操作或系统超时造成的。",
"timeout": "请求耗时太长。请检查您的网络或稍后再试。"
}
}
}
@@ -895,8 +895,8 @@
"message": "该路径可能不正确"
},
"tooManyRequests": {
"title": "",
"message": ""
"title": "指定时间内请求过多",
"message": "请求太多了。您可能会受到速率限制或被目标系统拒绝"
}
}
},
@@ -1004,7 +1004,7 @@
"cancel": "取消",
"delete": "删除",
"discard": "Discard",
"close": "",
"close": "关闭",
"confirm": "确认",
"continue": "继续",
"previous": "上一步",
@@ -1220,7 +1220,7 @@
"label": "集成"
},
"title": {
"label": ""
"label": "标题"
},
"customCssClasses": {
"label": "自定义 CSS 类"
@@ -1757,12 +1757,18 @@
"memory": {
"label": "显示内存信息"
},
"showUptime": {
"label": "显示运行时间"
},
"fileSystem": {
"label": "显示文件系统信息"
},
"defaultTab": {
"label": "默认标签"
},
"visibleClusterSections": {
"label": "可见集群部分"
},
"sectionIndicatorRequirement": {
"label": "部分指标要求"
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "Docker 状态",
"description": "您的容器状态 (这个小部件只能用管理员权限添加)",
"option": {},
"error": {
"internalServerError": "获取容器统计信息失败"
}
},
"common": {
"location": {
"query": "城市/邮编",
@@ -1898,8 +1912,8 @@
"description": "显示媒体服务器上的当前流",
"option": {
"showOnlyPlaying": {
"label": "",
"description": ""
"label": "只显示当前正在播放",
"description": "禁用此功能对plex不起作用"
}
},
"items": {
@@ -1932,7 +1946,7 @@
"label": "显示标记为已完成的 Torrent 条目"
},
"showCompletedHttp": {
"label": ""
"label": "显示标记为已完成的杂项"
},
"activeTorrentThreshold": {
"label": "隐藏在此阈值下已完成的 Torrent (kiB/s)"
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "使用过滤器来计算比率"
},
"limitPerIntegration": {
"label": "限制每个集成的项目",
"description": "这将限制每个集成显示的项目数量,而不是全局项目"
}
},
"errors": {
@@ -2077,7 +2095,7 @@
"approved": "已批准",
"declined": "已拒绝",
"failed": "失败",
"completed": ""
"completed": "已完成"
},
"toBeDetermined": "待定"
},
@@ -2181,94 +2199,106 @@
}
},
"releases": {
"name": "",
"description": "",
"name": "发布",
"description": "显示特定版本正则表达式中当前版本的列表。",
"option": {
"newReleaseWithin": {
"label": "",
"description": ""
"label": "内部新版本",
"description": "使用示例:1w(1周),10M(10个月)。支持单位类型h(小时),d(天),w(周),M(月),y(年)。留空不突出显示新版本。"
},
"staleReleaseWithin": {
"label": "",
"description": ""
"label": "内部过期发布",
"description": "使用示例:1w(1周),10M(10个月)。支持单位类型h(小时),d(天),w(周),M(月),y(年)。留空不突出显示过时的版本。"
},
"showOnlyHighlighted": {
"label": "",
"description": ""
"label": "只显示高亮显示",
"description": "只显示新的或过时的版本。如上所述。"
},
"showDetails": {
"label": ""
"label": "展开详情"
},
"topReleases": {
"label": "",
"description": ""
"label": "热门发布",
"description": "要显示的最新版本的最大数量。零表示没有限制。"
},
"repositories": {
"label": "",
"addRRepository": {
"label": ""
"label": "仓库",
"addRepository": {
"label": "添加仓库"
},
"importRepositories": {
"label": "从 Docker 导入",
"loading": "正在加载 docker 映像",
"noImagesFound": "没有找到 docker 映像",
"listFoundImages": "找到的映像列表",
"listAlreadyImportedImages": "已导入的映像列表",
"allImagesAlreadyImported": "所有映像已导入",
"onlyAdminCanImport": "只有管理员才能从 docker 导入"
},
"provider": {
"label": ""
"label": "提供者"
},
"identifier": {
"label": "",
"placeholder": ""
"label": "标识符",
"placeholder": "姓名或所有者名称"
},
"name": {
"label": ""
"label": "名称"
},
"versionFilter": {
"label": "",
"label": "版本过滤器",
"prefix": {
"label": ""
"label": "前缀"
},
"precision": {
"label": "",
"label": "精度",
"options": {
"none": ""
"none": "空"
}
},
"suffix": {
"label": ""
"label": "后缀"
},
"regex": {
"label": ""
"label": "正则表达式"
}
},
"edit": {
"label": ""
"label": "编辑"
},
"editForm": {
"title": "",
"title": "编辑仓库",
"cancel": {
"label": ""
"label": "取消"
},
"confirm": {
"label": ""
"label": "确认"
}
},
"example": {
"label": ""
"importForm": {
"title": "从 Docker 导入"
},
"invalid": ""
"example": {
"label": "示例"
},
"invalid": "仓库定义无效,请检查值"
}
},
"not-found": "",
"pre-release": "",
"archived": "",
"forked": "",
"starsCount": "",
"forksCount": "",
"issuesCount": "",
"openProjectPage": "",
"openReleasePage": "",
"releaseDescription": "",
"created": "",
"not-found": "未找到",
"pre-release": "预发行",
"archived": "已归档",
"forked": "复刻成功",
"starsCount": "星",
"forksCount": "复刻",
"issuesCount": "打开问题",
"openProjectPage": "打开项目页面",
"openReleasePage": "打开发布页面",
"releaseDescription": "发布说明",
"created": "已创建",
"error": {
"label": "",
"label": "错误",
"options": {
"noMatchingVersion": ""
"noMatchingVersion": "没有找到匹配的版本"
}
}
},
@@ -2276,56 +2306,56 @@
"option": {},
"card": {
"vpn": {
"countConnected": ""
"countConnected": "{count} 已连接"
}
},
"error": {
"integrationsDisconnected": "",
"unknownContentOption": ""
"integrationsDisconnected": "没有可用的数据,所有集成已断开",
"unknownContentOption": "网络控制器摘要小部件的未知内容选项: "
},
"name": "",
"description": ""
"name": "网络控制器概述",
"description": "显示网络控制器摘要(如Unifi Controller)"
},
"networkControllerStatus": {
"card": {
"variants": {
"wired": {
"name": ""
"name": "有线"
},
"wifi": {
"name": ""
"name": "Wi-Fi"
}
},
"users": {
"label": ""
"label": "用户数"
},
"guests": {
"label": ""
"label": "游客"
}
},
"option": {
"content": {
"option": {
"wifi": {
"label": ""
"label": "Wi-Fi"
},
"wired": {
"label": ""
"label": "有线"
}
},
"label": ""
"label": "小部件内容"
}
},
"error": {
"integrationsDisconnected": "",
"unknownContentOption": ""
"integrationsDisconnected": "没有可用的数据,所有集成已断开",
"unknownContentOption": "网络状态小部件的未知内容选项: "
},
"name": "",
"description": ""
"name": "网络状态",
"description": "在网络上显示已连接的设备"
},
"networkController": {
"error": {
"internalServerError": ""
"internalServerError": "获取网络控制器概述失败"
}
}
},
@@ -2448,10 +2478,10 @@
},
"backgroundImageUrl": {
"label": "背景图片 URL",
"placeholder": "",
"placeholder": "开始输入以搜索本地映像",
"group": {
"your": "你的图像",
"other": ""
"your": "你的映像",
"other": "其他映像"
}
},
"backgroundImageAttachment": {
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": "网络控制器"
},
"dockerContainers": {
"label": "Docker 容器"
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "已更新 {when}",
"search": "搜索 {count} 容器",
"selected": "{totalCount} 容器的 {selectCount}"
"selected": "{totalCount} 容器的 {selectCount}",
"footer": "总计 {count} 容器"
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": "废弃"
}
},
"stats": {
"cpu": {
"label": "CPU"
},
"memory": {
"label": "内存"
}
},
"containerImage": {
"label": "镜像"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "操作",
"start": {
"label": "开始",
"notification": {
@@ -3625,7 +3668,7 @@
"certificates": {
"label": "证书",
"hostnames": {
"label": ""
"label": "主机名"
}
}
},
@@ -4026,25 +4069,25 @@
"certificate": {
"field": {
"hostname": {
"label": ""
"label": "主机名"
},
"subject": {
"label": ""
"label": "主题"
},
"issuer": {
"label": ""
"label": "发行者"
},
"validFrom": {
"label": ""
"label": "有效期自"
},
"validTo": {
"label": ""
"label": "有效期至"
},
"serialNumber": {
"label": ""
"label": "序号"
},
"fingerprint": {
"label": ""
"label": "指纹"
}
},
"page": {
@@ -4055,19 +4098,19 @@
"title": "还没有证书"
},
"invalid": {
"title": "",
"description": ""
"title": "无效证书",
"description": "解析证书失败"
},
"expires": "到期时间 {when}",
"toHostnames": ""
"toHostnames": "信任的主机名"
},
"hostnames": {
"title": "",
"description": "",
"title": "信任的证书主机名",
"description": "某些证书不允许指定域名使用 Homarr 请求他们, 由于这个原因,所有信任的主机名及其证书缩略图都被用来绕过这些限制。",
"noResults": {
"title": ""
"title": "还没有主机名"
},
"toCertificates": ""
"toCertificates": "证书"
}
},
"action": {
@@ -4099,16 +4142,16 @@
}
},
"removeHostname": {
"label": "",
"confirm": "",
"label": "删除受信任的主机名",
"confirm": "您确定要删除此受信任的主机名吗?这可能会导致一些集成停止工作。",
"notification": {
"success": {
"title": "",
"message": ""
"title": "主机名已删除",
"message": "主机名已成功删除"
},
"error": {
"title": "",
"message": ""
"title": "主机名未删除",
"message": "无法删除主机名"
}
}
}
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Zobrazit informace o paměti"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Zobrazit informace o souborovém systému"
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Obraz"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Spustit",
"notification": {
+170 -127
View File
@@ -705,125 +705,125 @@
"error": {
"common": {
"cause": {
"title": ""
"title": "Årsag med flere detaljer"
}
},
"unknown": {
"title": "",
"description": ""
"title": "Ukendt fejl",
"description": "En ukendt fejl opstod, åbn årsagen nedenfor for at se flere detaljer"
},
"parse": {
"title": "",
"description": ""
"title": "Parsingfejl",
"description": "Svaret kunne ikke fortolkes. Kontroller, at URL'en peger på tjenestens grundlæggende URL."
},
"authorization": {
"title": "",
"description": ""
"title": "Godkendelsesfejl",
"description": "Anmodningen blev ikke godkendt. Kontroller, at legitimationsoplysningerne er korrekte og at du har dem konfigureret med nok tilladelser."
},
"statusCode": {
"title": "",
"description": "",
"otherDescription": "",
"title": "Svar fejl",
"description": "Modtaget uventet {statusCode} ({reason}) svar fra <url></url>. Kontroller, at URL'en peger på basis-URL'en for integrationen.",
"otherDescription": "Modtaget uventet {statusCode} svar fra <url></url>. Kontroller, at URLen peger på den grundlæggende URL for integrationen.",
"reason": {
"badRequest": "",
"notFound": "",
"tooManyRequests": "",
"internalServerError": "",
"serviceUnavailable": "",
"gatewayTimeout": ""
"badRequest": "Ugyldig forespørgsel",
"notFound": "Ikke fundet",
"tooManyRequests": "For mange forespørgsler",
"internalServerError": "Intern serverfejl",
"serviceUnavailable": "Tjeneste ikke tilgængelig",
"gatewayTimeout": "Gateway-timeout"
}
},
"certificate": {
"title": "",
"title": "Certifikatfejl",
"description": {
"expired": "",
"notYetValid": "",
"untrusted": "",
"hostnameMismatch": ""
"expired": "Certifikatet er udløbet.",
"notYetValid": "Certifikatet er ikke gyldigt endnu.",
"untrusted": "Der er ikke tillid til certifikatet.",
"hostnameMismatch": "Certifikatets værtsnavn matcher ikke URL'en."
},
"alert": {
"permission": {
"title": "",
"message": ""
"title": "Ikke tilstrækkelige tilladelser",
"message": "Du har ikke tilladelse til at stole på eller uploade certifikater. Kontakt venligst din administrator for at uploade det nødvendige rodcertifikat."
},
"hostnameMismatch": {
"title": "",
"message": ""
"title": "Værtsnavn matcher ikke",
"message": "Værtsnavnet i certifikatet matcher ikke det værtsnavn du forbinder til. Dette kan indikere en sikkerhedsrisiko, men du kan stadig vælge at stole på dette certifikat."
},
"extract": {
"title": "",
"message": ""
"title": "Ekstraktion af CA-certifikat mislykkedes",
"message": "Kun selvsignerede certifikater uden kæde kan hentes automatisk. Hvis du bruger et selvsigneret certifikat, skal du sørge for at uploade CA-certifikatet manuelt. Du kan finde instruktioner om, hvordan du gør dette <docsLink></docsLink>."
}
},
"action": {
"retry": {
"label": ""
"label": "Gentag oprettelse"
},
"trust": {
"label": ""
"label": "Stol på certifikat"
},
"upload": {
"label": ""
"label": "Upload certifikat"
}
},
"hostnameMismatch": {
"confirm": {
"title": "",
"message": ""
"title": "Stol på uoverensstemmelse mellem værtsnavn",
"message": "Er du sikker på du vil have tillid til certifikatet med et værtsnavn uoverensstemmelse?"
},
"notification": {
"success": {
"title": "",
"message": ""
"title": "Betroet certifikat",
"message": "Tilføjet værtsnavn til liste over betroede certifikater"
},
"error": {
"title": "",
"message": ""
"title": "Kunne ikke stole på certifikat",
"message": "Certifikatet med et værtsnavn mismatch kunne ikke stoles på"
}
}
},
"selfSigned": {
"confirm": {
"title": "",
"message": ""
"title": "Stol på selvsigneret certifikat",
"message": "Er du sikker på, at du vil stole på dette selvsignerede certifikat?"
},
"notification": {
"success": {
"title": "",
"message": ""
"title": "Betroet certifikat",
"message": "Tilføjet certifikat til liste over betroede certifikater"
},
"error": {
"title": "",
"message": ""
"title": "Kunne ikke stole på certifikat",
"message": "Kunne ikke tilføje certifikat til listen over betroede certifikater"
}
}
},
"details": {
"title": "",
"description": "",
"title": "Detaljer",
"description": "Gennemgå certifikatoplysningerne, før du beslutter dig for at stole på den.",
"content": {
"action": "",
"title": ""
"action": "Vis indhold",
"title": "PEM Certifikat"
}
}
},
"request": {
"title": "",
"title": "Anmodningsfejl",
"description": {
"connection": {
"hostUnreachable": "",
"networkUnreachable": "",
"refused": "",
"reset": ""
"hostUnreachable": "Serveren kunne ikke nås. Dette betyder normalt, at værten er offline eller ikke kan nås fra dit netværk.",
"networkUnreachable": "Netværket er ikke tilgængeligt. Tjek venligst din internetforbindelse eller netværkskonfiguration.",
"refused": "Serveren nægtede forbindelsen. Den kører muligvis ikke eller afviser forespørgsler på den angivne port.",
"reset": "Forbindelsen blev uventet lukket af serveren. Dette kan ske hvis serveren er ustabil eller genstartet."
},
"dns": {
"notFound": "",
"timeout": "",
"noAnswer": ""
"notFound": "Serveradressen kunne ikke findes. Tjek venligst URL'en for stave eller ugyldige domænenavne.",
"timeout": "DNS-opslag fik timeout. Dette kan være et midlertidigt problem - prøv igen om et øjeblik.",
"noAnswer": "DNS-serveren returnerede ikke et gyldigt svar. Domænet findes måske men har ingen gyldige poster."
},
"timeout": {
"aborted": "",
"timeout": ""
"aborted": "Anmodningen blev afbrudt før den kunne fuldføres. Dette kan skyldes en bruger handling eller system timeout.",
"timeout": "Forespørgslen tog for lang tid at fuldføre og fik timeout. Tjek dit netværk eller prøv igen senere."
}
}
}
@@ -1004,7 +1004,7 @@
"cancel": "Annuller",
"delete": "Slet",
"discard": "Kassér",
"close": "",
"close": "Luk",
"confirm": "Bekræft",
"continue": "Forsæt",
"previous": "Forrige",
@@ -1220,7 +1220,7 @@
"label": "Integrationer"
},
"title": {
"label": ""
"label": "Titel"
},
"customCssClasses": {
"label": "Brugerdefinerede CSS-klasser"
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Vis hukommelsesoplysninger"
},
"showUptime": {
"label": "Vis Oppetid"
},
"fileSystem": {
"label": "Vis information om filsystemet"
},
"defaultTab": {
"label": "Standard fane"
},
"visibleClusterSections": {
"label": "Synlige klynge sektioner"
},
"sectionIndicatorRequirement": {
"label": "Sektion indikator er et krav"
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "Docker statistik",
"description": "Statistik for dine containere (Denne widget kan kun tilføjes med administratorrettigheder)",
"option": {},
"error": {
"internalServerError": "Kunne ikke hente containerstatistik"
}
},
"common": {
"location": {
"query": "By / Postnummer",
@@ -1898,8 +1912,8 @@
"description": "Vis de aktuelle streams på dine medieservere",
"option": {
"showOnlyPlaying": {
"label": "",
"description": ""
"label": "Vis kun hvad der aktuelt afspilles",
"description": "Deaktivering af dette vil ikke virke for plex"
}
},
"items": {
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "Brug filteret til at beregne forholdet"
},
"limitPerIntegration": {
"label": "Begræns elementer pr. integration",
"description": "Dette vil begrænse antallet af elementer vist pr. integration, ikke globalt"
}
},
"errors": {
@@ -2077,7 +2095,7 @@
"approved": "Godkendt",
"declined": "Afvist",
"failed": "Mislykket",
"completed": ""
"completed": "Fuldført"
},
"toBeDetermined": "TBD"
},
@@ -2181,94 +2199,106 @@
}
},
"releases": {
"name": "",
"description": "",
"name": "Udgivelser",
"description": "Viser en liste over den aktuelle version af de givne depoter med den givne version regex.",
"option": {
"newReleaseWithin": {
"label": "",
"description": ""
"label": "Ny Udgivelse Inden",
"description": "Anvendelseseksempel: 1w (1 uge), 10M (10 måneder). Accepterede enhedstyper h (timer), d (dage), w (uger), M (måneder), y (år). Lad være tom for ingen fremhævning af nye udgivelser."
},
"staleReleaseWithin": {
"label": "",
"description": ""
"label": "Gamle Udgivelse Indenfor",
"description": "Anvendelseseksempel: 1w (1 uge), 10M (10 måneder). Accepterede enhedstyper h (timer), d (dage), w (uger), M (måneder), y (år). Lad være tom for ingen fremhævning af forældede udgivelser."
},
"showOnlyHighlighted": {
"label": "",
"description": ""
"label": "Vis Kun Fremhævede",
"description": "Vis kun nye eller forældede udgivelser. Som pr ovenfor."
},
"showDetails": {
"label": ""
"label": "Vis detaljer"
},
"topReleases": {
"label": "",
"description": ""
"label": "Top Udgivelser",
"description": "Det maksimale antal seneste udgivelser at vise. Nul betyder ingen grænse."
},
"repositories": {
"label": "",
"addRRepository": {
"label": ""
"label": "Repositories",
"addRepository": {
"label": "Tilføj repository"
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
"label": "Udbyder"
},
"identifier": {
"label": "",
"placeholder": ""
"label": "Identifikator",
"placeholder": "Navn eller ejer/navn"
},
"name": {
"label": ""
"label": "Navn"
},
"versionFilter": {
"label": "",
"label": "Versionsfilter",
"prefix": {
"label": ""
"label": "Præfiks"
},
"precision": {
"label": "",
"label": "Præcision",
"options": {
"none": ""
"none": "Ingen"
}
},
"suffix": {
"label": ""
"label": "Suffiks"
},
"regex": {
"label": ""
"label": "Regulært Udtryk"
}
},
"edit": {
"label": ""
"label": "Rediger"
},
"editForm": {
"title": "",
"title": "Rediger Repository",
"cancel": {
"label": ""
"label": "Annuller"
},
"confirm": {
"label": ""
"label": "Bekræft"
}
},
"example": {
"label": ""
"importForm": {
"title": ""
},
"invalid": ""
"example": {
"label": "Eksempel"
},
"invalid": "Ugyldig repository definition, tjek venligst værdierne"
}
},
"not-found": "",
"pre-release": "",
"archived": "",
"forked": "",
"starsCount": "",
"forksCount": "",
"issuesCount": "",
"openProjectPage": "",
"openReleasePage": "",
"releaseDescription": "",
"created": "",
"not-found": "Ikke fundet",
"pre-release": "Pre-Release",
"archived": "Arkiveret",
"forked": "Forked",
"starsCount": "Stjerner",
"forksCount": "Forks",
"issuesCount": "Åbne Problemer",
"openProjectPage": "Åbn Projektside",
"openReleasePage": "Åbn Udgivelsesside",
"releaseDescription": "Udgivelse Beskrivelse",
"created": "Oprettet",
"error": {
"label": "",
"label": "Fejl",
"options": {
"noMatchingVersion": ""
"noMatchingVersion": "Ingen matchende version fundet"
}
}
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": "Netværkskontroller"
},
"dockerContainers": {
"label": "Docker containers"
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "Opdateret {when}",
"search": "Søg {count} containere",
"selected": "{selectCount} af {totalCount} containere valgt"
"selected": "{selectCount} af {totalCount} containere valgt",
"footer": "Total {count} containere"
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": "Død"
}
},
"stats": {
"cpu": {
"label": "CPU"
},
"memory": {
"label": "Hukommelse"
}
},
"containerImage": {
"label": "Billede"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "Handlinger",
"start": {
"label": "Start",
"notification": {
@@ -3625,7 +3668,7 @@
"certificates": {
"label": "Certifikater",
"hostnames": {
"label": ""
"label": "Værtsnavne"
}
}
},
@@ -4026,25 +4069,25 @@
"certificate": {
"field": {
"hostname": {
"label": ""
"label": "Værtsnavn"
},
"subject": {
"label": ""
"label": "Emne"
},
"issuer": {
"label": ""
"label": "Udsteder"
},
"validFrom": {
"label": ""
"label": "Gyldig fra"
},
"validTo": {
"label": ""
"label": "Gyldig til"
},
"serialNumber": {
"label": ""
"label": "Serienummer"
},
"fingerprint": {
"label": ""
"label": "Fingeraftryk"
}
},
"page": {
@@ -4055,19 +4098,19 @@
"title": "Der er endnu ingen certifikater"
},
"invalid": {
"title": "",
"description": ""
"title": "Ugyldigt certifikat",
"description": "Kunne ikke fortolke certifikat"
},
"expires": "Udløber {when}",
"toHostnames": ""
"toHostnames": "Betroede værtsnavne"
},
"hostnames": {
"title": "",
"description": "",
"title": "Betroet certifikat værtsnavne",
"description": "Nogle certifikater tillader ikke det specifikke domæne Homarr bruger til at anmode om dem, på grund af dette alle betroede værtsnavne med deres certifikat thumbprints bruges til at omgå disse restriktioner.",
"noResults": {
"title": ""
"title": "Der er endnu ingen værtsnavne"
},
"toCertificates": ""
"toCertificates": "Certifikater"
}
},
"action": {
@@ -4099,16 +4142,16 @@
}
},
"removeHostname": {
"label": "",
"confirm": "",
"label": "Fjern betroet værtsnavn",
"confirm": "Er du sikker på du vil fjerne dette betroede værtsnavn? Dette kan få nogle integrationer til at stoppe med at virke.",
"notification": {
"success": {
"title": "",
"message": ""
"title": "Værtsnavn fjernet",
"message": "Værtsnavnet blev fjernet korrekt"
},
"error": {
"title": "",
"message": ""
"title": "Værtsnavn ikke fjernet",
"message": "Værtsnavnet kunne ikke fjernes"
}
}
}
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Speicher-Info anzeigen"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Dateisystem Info anzeigen"
},
"defaultTab": {
"label": "Standard Tab"
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": "Anforderung der Sektionsindikatoren"
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "Stadt / Postleitzahl",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "Filter zur Berechnung des Verhältnisses verwenden"
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "Aktualisiert {when}",
"search": "{count} Container durchsuchen",
"selected": "{selectCount} von {totalCount} ausgewählten Containern"
"selected": "{selectCount} von {totalCount} ausgewählten Containern",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": "Tot"
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": ""
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Starten",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Speicher-Info anzeigen"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Dateisystem Info anzeigen"
},
"defaultTab": {
"label": "Standard Tab"
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": "Anforderung der Sektionsindikatoren"
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "Stadt / Postleitzahl",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "Filter zur Berechnung des Verhältnisses verwenden"
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "Repositories",
"addRRepository": {
"addRepository": {
"label": "Repository hinzufügen"
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": "Anbieter"
},
@@ -2248,6 +2275,9 @@
"label": "Bestätigen"
}
},
"importForm": {
"title": ""
},
"example": {
"label": "Beispiel"
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": "Netzwerk Controller"
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "Aktualisiert {when}",
"search": "{count} Container durchsuchen",
"selected": "{selectCount} von {totalCount} ausgewählten Containern"
"selected": "{selectCount} von {totalCount} ausgewählten Containern",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": "Tot"
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Image"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Starten",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Εμφάνιση Πληροφοριών Μνήμης"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Εμφάνιση Πληροφοριών Συστήματος Αρχείων"
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Εικόνα"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Έναρξη",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": ""
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": ""
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": ""
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "",
"notification": {
+23 -1
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Show Memory Info"
},
"showUptime": {
"label": "Show Uptime"
},
"fileSystem": {
"label": "Show Filesystem Info"
},
"defaultTab": {
"label": "Default tab"
},
"visibleClusterSections": {
"label": "Visible cluster sections"
},
"sectionIndicatorRequirement": {
"label": "Section indicator requirement"
}
@@ -1953,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "Use filter to calculate Ratio"
},
"limitPerIntegration": {
"label": "Limit items per integration",
"description": "This will limit the number of items shown per integration, not globally"
}
},
"errors": {
@@ -2213,9 +2223,18 @@
},
"repositories": {
"label": "Repositories",
"addRRepository": {
"addRepository": {
"label": "Add repository"
},
"importRepositories": {
"label": "Import from docker",
"loading": "Loading docker images",
"noImagesFound": "No docker images found",
"listFoundImages": "List of found images",
"listAlreadyImportedImages": "List of already imported images",
"allImagesAlreadyImported": "All images already imported",
"onlyAdminCanImport": "Only administrators can import from docker"
},
"provider": {
"label": "Provider"
},
@@ -2256,6 +2275,9 @@
"label": "Confirm"
}
},
"importForm": {
"title": "Import from Docker"
},
"example": {
"label": "Example"
},
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Mostrar información de la memoria"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Mostrar información del sistema de archivos"
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Imagen"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Iniciar",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": ""
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": ""
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": ""
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "",
"notification": {
+46 -3
View File
@@ -747,7 +747,7 @@
"message": ""
},
"hostnameMismatch": {
"title": "",
"title": "Nom d'hôte incohérent",
"message": ""
},
"extract": {
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Afficher les infos de la mémoire"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Afficher les infos sur le système de fichiers"
},
"defaultTab": {
"label": "Onglet par défaut"
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": "Exigence de l'indicateur de section"
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "Ville / Code Postal",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "Utiliser le filtre pour calculer le ratio"
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "Mis à jour {when}",
"search": "Rechercher dans {count} conteneurs",
"selected": "{selectCount} sur {totalCount} conteneurs sélectionnés"
"selected": "{selectCount} sur {totalCount} conteneurs sélectionnés",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": "Mort"
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Image"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Début",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "הצג מידע זיכרון"
},
"showUptime": {
"label": "הצג זמן פעולה"
},
"fileSystem": {
"label": "הצג מידע על מערכת הקבצים"
},
"defaultTab": {
"label": "כרטיסיית ברירת מחדל"
},
"visibleClusterSections": {
"label": "מקטעי אשכול גלויים"
},
"sectionIndicatorRequirement": {
"label": "דרישת מציין מקטע"
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "סטטיסטיקות דוקר",
"description": "סטטיסטיקות של המכולות שלך (ניתן להוסיף ווידג'ט זה רק עם הרשאות מנהל)",
"option": {},
"error": {
"internalServerError": "נכשל באחזור סטטיסטיקות המכולות"
}
},
"common": {
"location": {
"query": "עיר / מיקוד",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": "השתמש במסנן כדי לחשב יחס"
},
"limitPerIntegration": {
"label": "הגבלת פריטים לכל אינטגרציה",
"description": "פעולה זו תגביל את מספר הפריטים המוצגים בכל אינטגרציה, לא באופן גלובלי"
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "מאגרים",
"addRRepository": {
"addRepository": {
"label": "הוסף מאגר"
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": "ספק"
},
@@ -2248,6 +2275,9 @@
"label": "אשר"
}
},
"importForm": {
"title": ""
},
"example": {
"label": "דוגמא"
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": "בקר רשת"
},
"dockerContainers": {
"label": "מכולות דוקר"
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "עודכן ב- {when}",
"search": "חפש {count} קונטיינרים",
"selected": "{selectCount} מ- {totalCount} קונטיינרים נבחרו"
"selected": "{selectCount} מ- {totalCount} קונטיינרים נבחרו",
"footer": "סה״כ {count} קונטיינרים"
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": "מת"
}
},
"stats": {
"cpu": {
"label": "מעבד"
},
"memory": {
"label": "זיכרון"
}
},
"containerImage": {
"label": "קובץ תמונה"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "פעולות",
"start": {
"label": "התחל",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": ""
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": ""
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Slika"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Pokreni",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Memóriainformációk megjelenítése"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Fájlrendszer-információk megjelenítése"
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Kép"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Indítás",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "Mostra Informazioni Memoria"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "Mostra Informazioni Filesystem"
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "Immagine"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "Avvia",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": "メモリー情報を表示"
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": "ファイルシステム情報を表示"
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "画像"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "開始",
"notification": {
+45 -2
View File
@@ -1757,12 +1757,18 @@
"memory": {
"label": ""
},
"showUptime": {
"label": ""
},
"fileSystem": {
"label": ""
},
"defaultTab": {
"label": ""
},
"visibleClusterSections": {
"label": ""
},
"sectionIndicatorRequirement": {
"label": ""
}
@@ -1832,6 +1838,14 @@
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"option": {},
"error": {
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -1945,6 +1959,10 @@
},
"applyFilterToRatio": {
"label": ""
},
"limitPerIntegration": {
"label": "",
"description": ""
}
},
"errors": {
@@ -2205,9 +2223,18 @@
},
"repositories": {
"label": "",
"addRRepository": {
"addRepository": {
"label": ""
},
"importRepositories": {
"label": "",
"loading": "",
"noImagesFound": "",
"listFoundImages": "",
"listAlreadyImportedImages": "",
"allImagesAlreadyImported": "",
"onlyAdminCanImport": ""
},
"provider": {
"label": ""
},
@@ -2248,6 +2275,9 @@
"label": ""
}
},
"importForm": {
"title": ""
},
"example": {
"label": ""
},
@@ -3091,6 +3121,9 @@
},
"networkController": {
"label": ""
},
"dockerContainers": {
"label": ""
}
}
},
@@ -3155,7 +3188,8 @@
"table": {
"updated": "",
"search": "",
"selected": ""
"selected": "",
"footer": ""
},
"field": {
"name": {
@@ -3173,6 +3207,14 @@
"dead": ""
}
},
"stats": {
"cpu": {
"label": ""
},
"memory": {
"label": ""
}
},
"containerImage": {
"label": "이미지"
},
@@ -3181,6 +3223,7 @@
}
},
"action": {
"title": "",
"start": {
"label": "시작",
"notification": {
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more