Compare commits

...
Author SHA1 Message Date
Meier Lukas c2ddb676fe Merge branch 'dev' into fix-stacked-dynamic-sections-menu 2024-10-26 18:06:22 +02:00
homarr-renovate[bot] f8c21f6000 fix(deps): update dependency tldts to ^6.1.56 (#1375)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-26 10:48:09 +00:00
homarr-renovate[bot] 3127182f5c fix(deps): update dependency eslint-plugin-jsx-a11y to ^6.10.2 (#1374)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-26 05:11:06 +00:00
homarr-renovate[bot] 0c41241bbd chore(deps): update dependency @types/node to ^20.17.1 (#1371)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-25 13:39:34 +00:00
homarr-renovate[bot] 4985bd8f91 fix(deps): update dependency video.js to ^8.19.1 (#1370)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-25 07:37:26 +00:00
homarr-renovate[bot] 3a6e957710 fix(deps): update dependency tldts to ^6.1.55 (#1369)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-24 23:27:09 +00:00
Meier Lukas 041006c181 fix: docker build not working (#1365) 2024-10-24 22:45:36 +02:00
homarr-renovate[bot] 2bac28a50a fix(deps): update dependency sass to ^1.80.4 (#1366)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 23:11:15 +00:00
homarr-renovate[bot] f55b813aa1 fix(deps): update mantine monorepo to ^7.13.4 (#1364)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 17:39:32 +00:00
Meier LukasandYossi Hillali c37a0e38d9 fix: health-check widget sorting & last seen (#1363)
Co-authored-by: Yossi Hillali <950010+hillaliy@users.noreply.github.com>
2024-10-23 17:30:10 +02:00
homarr-renovate[bot] 32b0059b10 chore(deps): update dependency @types/node to ^20.17.0 (#1362)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 14:48:51 +00:00
homarr-renovate[bot] b542bad939 fix(deps): update tanstack-query monorepo to ^5.59.16 (#1361)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 14:42:10 +00:00
Yossi Hillali cf9b0581a4 feat: plex integration (#1342)
* feat: plex integration

* feat: plex integration

* fix: DeepSource error

* fix: lint error

* fix: pnpm-lock

* fix: lint error

* fix: errors

* fix: pnpm-lock

* fix: reviewed changes

* fix: reviewed changes

* fix: reviewed changes

* fix: pnpm-lock
2024-10-23 17:18:55 +03:00
homarr-renovate[bot] d4919dc908 fix(deps): update tiptap monorepo to v2.9.1 (#1360)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 09:40:01 +00:00
homarr-renovate[bot] 30d4464067 fix(deps): update dependency @tabler/icons-react to ^3.20.0 (#1359)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 06:16:16 +00:00
homarr-renovate[bot] 44176b403e chore(deps): update dependency @types/react to ^18.3.12 (#1358)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 04:49:15 +00:00
homarr-renovate[bot] f756d17901 chore(deps): update dependency @types/prismjs to ^1.26.5 (#1357)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 04:33:36 +00:00
homarr-renovate[bot] dea6d6c311 chore(deps): update dependency @types/node to ^20.16.15 (#1356)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 04:13:58 +00:00
homarr-renovate[bot] aea65eadcd fix(deps): update nextjs monorepo to ^14.2.16 (#1355)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-23 02:58:20 +00:00
homarr-renovate[bot] 1a2f0adfc8 fix(deps): update dependency eslint-plugin-react to ^7.37.2 (#1354)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-22 22:31:36 +00:00
homarr-renovate[bot] c3b9d981f7 fix(deps): update tiptap monorepo to v2.9.0 (#1352)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-22 21:27:12 +00:00
homarr-renovate[bot] e062ea1507 fix(deps): update dependency tldts to ^6.1.54 (#1353)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-22 21:10:26 +00:00
homarr-renovate[bot] a8c36f35c3 fix(deps): update dependency tldts to ^6.1.53 (#1351)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-22 06:51:30 +00:00
homarr-renovate[bot] d599390512 fix(deps): update dependency better-sqlite3 to ^11.5.0 (#1350)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-22 03:42:12 +00:00
homarr-renovate[bot] 4e41af51eb chore(deps): update dependency @types/node to ^20.16.14 (#1349)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-22 03:23:32 +00:00
homarr-renovate[bot] 85a5ebd288 fix(deps): update dependency drizzle-orm to ^0.35.3 (#1348)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-21 21:26:30 +00:00
homarr-renovate[bot] 81372a81e2 chore(deps): update turbo monorepo to ^2.2.3 (#1345)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-21 17:48:38 +00:00
homarr-renovate[bot] a005ead158 fix(deps): update dependency typescript-eslint to ^8.11.0 (#1346)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-21 17:39:58 +00:00
Meier Lukas 2f1c800844 feat: add everyone group (#1322)
* feat: add everyone group through seed

* feat: add reserved group name check in group router actions

* feat: improve user interface for everyone group

* fix: reserved group alert is a server component

* feat: add all users to everyone group

* chore: update lockfile

* fix: format issues

* fix: lint issues

* fix: lint format issues

* test: add unit tests for everyone group

* refactor: add codegen for documentation urls by sitemap

* refactor: change group query to count

* chore: remove migrations temporarily

* chore: add migrations again

* chore: add lint rule to prevent usage of raw documentation links

* fix: format issues
2024-10-21 17:23:51 +02:00
homarr-renovate[bot] 654880d7e4 chore(deps): update turbo monorepo to ^2.2.2 (#1344)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-21 14:32:05 +00:00
Yossi Hillali 9a7e845010 feat: RTL option to RSS widget (#1247) 2024-10-21 13:19:34 +03:00
homarr-renovate[bot] 564096e463 fix(deps): update dependency eslint-plugin-jsx-a11y to ^6.10.1 (#1343)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-21 06:45:21 +00:00
homarr-renovate[bot] 278107a1a4 fix(deps): update dependency next-auth to v5.0.0-beta.25 (#1341)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 21:10:50 +00:00
homarr-renovate[bot] c5cc21a4b0 fix(deps): update dependency @auth/drizzle-adapter to ^1.7.2 (#1339)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 20:40:19 +00:00
homarr-renovate[bot] aa2d2f8c4b fix(deps): update dependency @auth/core to ^0.37.2 (#1338)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 20:29:37 +00:00
homarr-renovate[bot] 9b5b8e6db3 chore(deps): update dependency @vitejs/plugin-react to ^4.3.3 (#1336)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 16:33:23 +00:00
Meier Lukas 9c5972faf4 fix: missing migration for default colorscheme change (#1335)
* fix: missing migration for default colorscheme change

* fix: format issues
2024-10-19 17:57:00 +02:00
homarr-renovate[bot] 98f1c33318 fix(deps): update dependency react-error-boundary to ^4.1.2 (#1334)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 14:29:30 +00:00
homarr-renovate[bot] 6e24553e10 chore(deps): update turbo monorepo to ^2.2.1 (#1333)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 13:16:08 +00:00
Meier Lukas 799d6c5d4a fix: color scheme manager should use cookie and not session value (#1329)
* fix: color scheme manager should use cookie and not session value

* fix: cookie saved for random path
2024-10-19 10:27:31 +02:00
Meier Lukas 7572634780 fix: local websocket url wrong (#1328) 2024-10-19 10:27:13 +02:00
homarr-renovate[bot] 7e219bc86f chore(deps): update dependency @types/node to ^20.16.13 (#1332)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 04:13:11 +00:00
homarr-renovate[bot] e46bf5d248 fix(deps): update dependency sass to ^1.80.3 (#1331)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-19 00:48:44 +00:00
homarr-renovate[bot] d40655134f chore(deps): update dependency eslint to ^9.13.0 (#1330)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-18 21:27:06 +00:00
homarr-renovate[bot] fa1a2c4b31 chore(deps): update turbo monorepo to ^2.2.0 (#1327)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-18 19:48:32 +00:00
Yossi Hillali ce67fcd57c fix: add subscription to health monitoring widget (#1210)
* fix: add subscription

* fix: add time stamped

* fix: rtl, timestamp, scrollArea

* fix: common.rtl

* fix: rtl

* fix: reviewed changes

* fix: translation

* fix: reviewed changes

* fix: deepScource

* fix: reviewed changes

* fix: add last seen
2024-10-18 21:42:33 +03:00
homarr-renovate[bot] c52fd972b7 fix(deps): update dependency next-auth to v5.0.0-beta.23 (#1325)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-18 17:48:55 +00:00
homarr-renovate[bot] b1393fa59b fix(deps): update dependency @auth/drizzle-adapter to ^1.7.1 (#1324)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-18 17:39:32 +00:00
homarr-renovate[bot] dac2b5063b fix(deps): update dependency @auth/core to ^0.37.1 (#1323)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-18 17:29:44 +00:00
homarr-renovate[bot] c8b1bb8d62 fix(deps): update dependency drizzle-orm to ^0.35.2 (#1321)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-18 11:48:33 +00:00
homarr-renovate[bot] 601af7acd8 fix(deps): update dependency better-sqlite3 to ^11.4.0 (#1320)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 21:26:58 +00:00
homarr-renovate[bot] 3781fe5cf8 fix(deps): update dependency sass to ^1.80.2 (#1319)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 21:10:42 +00:00
homarr-renovate[bot] a231a2da6c fix(deps): update dependency typescript-eslint to ^8.10.0 (#1317)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 20:29:44 +00:00
Meier Lukas 0df7cb5c25 feat: improve metadata (#1308) 2024-10-17 12:38:38 +02:00
homarr-renovate[bot] a44c907745 fix(deps): update dependency sass to ^1.80.1 (#1314)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 08:35:20 +00:00
homarr-renovate[bot] 84bb1deae5 fix(deps): update mantine monorepo to ^7.13.3 (#1316)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 08:15:09 +00:00
homarr-renovate[bot] 3b5082c107 fix(deps): update dependency react-error-boundary to ^4.1.1 (#1315)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 02:58:23 +00:00
homarr-renovate[bot] c57bf54bbb chore(deps): update dependency @types/node to ^20.16.12 (#1313)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-17 00:49:04 +00:00
homarr-renovate[bot] 0c134dde6e fix(deps): update dependency sass to ^1.79.6 (#1312)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-16 23:27:36 +00:00
Meier Lukas 727f10448f feat: add manifest (#1309) 2024-10-16 21:46:32 +02:00
Meier Lukas 12105b3b29 fix: inconsistent session cookie (#1310) 2024-10-16 21:46:12 +02:00
Meier Lukas cd77acdfab fix: prevent flickering by removing auto color scheme and default background color (#1299)
* fix: prevent flickering by removing auto color scheme and default background color

* fix: typecheck issue
2024-10-16 21:44:53 +02:00
Meier Lukas e99fd64882 feat: add notes for creation of apps and integrations in widget edit modal (#1297)
* feat: add notes for creation of apps and integrations in widget edit modal

* fix: unit test failing when with-description flag missing
2024-10-16 21:44:28 +02:00
Meier Lukas a0cc31da70 feat: populate integration name for new integrations automatically (#1296) 2024-10-16 21:44:12 +02:00
homarr-renovate[bot]andMeier Lukas a87c937b69 fix(deps): update dependency eslint-plugin-react-hooks to v5 (#1280)
* fix(deps): update dependency eslint-plugin-react-hooks to v5

* fix: lint issues after reenabling hook rules

* fix: format issues

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-10-16 21:43:51 +02:00
homarr-renovate[bot] ea43ed0ca4 chore(deps): update pnpm to v9.12.2 (#1311)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-16 15:30:28 +00:00
homarr-renovate[bot] c4ec4f3c66 fix(deps): update dependency tldts to ^6.1.52 (#1307)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-16 13:48:21 +00:00
homarr-renovate[bot] c053a373b4 fix(deps): update dependency drizzle-orm to ^0.35.1 (#1306)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-16 08:52:11 +00:00
homarr-renovate[bot] 9fa4bef2a4 fix(deps): update dependency react-error-boundary to ^4.1.0 (#1304)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-16 08:14:59 +00:00
homarr-renovate[bot] da13d63969 fix(deps): update dependency drizzle-kit to ^0.26.2 (#1305)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-16 07:48:32 +00:00
homarr-renovate[bot] f50f46d4b7 fix(deps): update dependency drizzle-kit to ^0.26.1 (#1303)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 20:29:39 +00:00
homarr-renovate[bot] c6daccbdf0 fix(deps): update dependency drizzle-orm to ^0.35.0 (#1302)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 19:48:50 +00:00
homarr-renovate[bot] f2f31b5ee0 fix(deps): update dependency drizzle-kit to ^0.26.0 (#1301)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 19:34:33 +00:00
Meier Lukas 476fda279d fix: integration actions hidden for full access users (#1300) 2024-10-15 21:11:32 +02:00
homarr-renovate[bot] bb0026a9fd fix(deps): update tanstack-query monorepo to ^5.59.15 (#1298)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 17:40:54 +00:00
homarr-renovate[bot] ba81e5e35a fix(deps): update dependency jotai to ^2.10.1 (#1295)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 15:11:33 +00:00
homarr-renovate[bot] df8f6d25d4 fix(deps): update tanstack-query monorepo to ^5.59.14 (#1282)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 07:38:17 +00:00
homarr-renovate[bot] c707b4b10f fix(deps): update dependency @million/lint to v1.0.11 (#1294)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-15 02:15:37 +00:00
homarr-renovate[bot] ed52b26f6d fix(deps): update dependency @million/lint to v1.0.10 (#1293)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-14 22:12:26 +00:00
homarr-renovate[bot] 65b1ae26d1 fix(deps): update dependency typescript-eslint to ^8.9.0 (#1292)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-14 17:40:37 +00:00
homarr-renovate[bot] 85cf101043 fix(deps): update dependency undici to v6.20.1 (#1291)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-14 12:58:46 +00:00
homarr-renovate[bot] bd31a4c3ad chore(deps): update vitest monorepo to ^2.1.3 (#1290)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-14 11:25:16 +00:00
Yossi Hillali 98b62a9f91 feat: radarr release type to calendar widget (#1256)
* feat: add release type

* fix: type check

* fix: deepSource

* fix: new approach

* fix: deepSource

* fix: typecheck

* fix: reviewed changes
2024-10-14 11:38:13 +03:00
homarr-renovate[bot] 1bc470a39f fix(deps): update dependency chroma-js to ^3.1.2 (#1289)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-13 10:41:00 +00:00
homarr-renovate[bot] 0fadc28a12 fix(deps): update dependency tldts to ^6.1.51 (#1288)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-12 09:28:18 +00:00
homarr-renovate[bot] 6f97741862 fix(deps): update dependency mantine-react-table to v2.0.0-beta.7 (#1287)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-12 05:11:06 +00:00
Meier Lukas 348687670d feat: user setting ping icons (#1277)
* feat: user setting ping icons

* fix: format issues

* test: adjust test to match expectations
2024-10-12 00:20:47 +02:00
Meier Lukas 0f8d9edb3e refactor: replace serverdata with suspense query (#1265)
* refactor: replace serverdata with suspense query

* fix: deepsource issues
2024-10-11 23:47:07 +02:00
Meier Lukas 511c9a4dbb fix: credentials auth not working (#1284) 2024-10-11 23:36:05 +02:00
Meier Lukas fe99421ec9 Merge branch 'dev' into fix-stacked-dynamic-sections-menu 2024-10-11 21:59:26 +02:00
homarr-renovate[bot] c051bf55b2 chore(deps): update dependency @types/react-dom to ^18.3.1 (#1281)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-11 19:22:46 +00:00
homarr-renovate[bot] 99ddd9214e fix(deps): update dependency sass to ^1.79.5 (#1279)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-11 02:13:06 +00:00
homarr-renovate[bot] 0ca140f4ff fix(deps): update tanstack-query monorepo to ^5.59.9 (#1278)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-10 19:23:01 +00:00
Meier Lukas 98cf214aed fix: groups can be created with empty names (#1267) 2024-10-09 17:23:19 +02:00
homarr-renovate[bot] efd06ea107 fix(deps): update tanstack-query monorepo to ^5.59.6 (#1276)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-09 14:49:27 +00:00
homarr-renovate[bot] c7b226142f fix(deps): update dependency undici to v6.20.0 (#1275)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-09 08:35:02 +00:00
homarr-renovate[bot] e3cd8a8b25 fix(deps): update tanstack-query monorepo to ^5.59.3 (#1274)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-09 06:40:45 +00:00
homarr-renovate[bot] 8bcba350d5 fix(deps): update dependency @auth/core to ^0.37.0 (#1272)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-09 02:13:10 +00:00
homarr-renovate[bot] a44ead09a3 fix(deps): update dependency @auth/drizzle-adapter to ^1.7.0 (#1273)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-09 00:48:35 +00:00
homarr-renovate[bot] 388717ec3c chore(deps): update dependency typescript to ^5.6.3 (#1271)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-08 22:31:09 +00:00
homarr-renovate[bot] 2bbe594cd2 fix(deps): update nextjs monorepo to ^14.2.15 (#1270)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-08 21:27:57 +00:00
homarr-renovate[bot] 6f2bc8e9be chore(deps): update dependency @types/node to ^20.16.11 (#1269)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 22:48:25 +00:00
homarr-renovate[bot] 14d1ce842a fix(deps): update dependency drizzle-orm to ^0.34.1 (#1268)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 20:31:36 +00:00
Meier Lukas ab1744ce20 feat: add first day of week user setting (#1249)
* feat: add first day of week user setting

* fix: add missing migrations

* fix: format and test issues

* fix: deepsource issue

* refactor: rename first-day-of-week procedure
2024-10-07 21:13:38 +02:00
Meier Lukas eb21628ee4 refactor: replace signIn callback with signIn event, adjust getUserByEmail in adapter to check provider (#1223)
* refactor: replace signIn callback with signIn event, adjust getUserByEmail in adapter to check provider

* test: adjusting tests for adapter and events

* docs: add comments for unknown auth provider

* fix: missing dayjs import
2024-10-07 21:13:15 +02:00
homarr-renovate[bot] 4d51e3b344 fix(deps): update dependency typescript-eslint to ^8.8.1 (#1266)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 17:39:10 +00:00
homarr-renovate[bot] 23ee229906 fix(deps): update dependency drizzle-orm to ^0.34.0 (#1262)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 15:30:13 +00:00
homarr-renovate[bot] 635fadfce9 fix(deps): update dependency drizzle-kit to ^0.25.0 (#1261)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 15:12:13 +00:00
homarr-renovate[bot] 2d5a7a26c1 chore(deps): update pnpm to v9.12.1 (#1260)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 13:48:27 +00:00
homarr-renovate[bot] ea84b1c372 fix(deps): update dependency video.js to ^8.18.1 (#1259)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 09:41:13 +00:00
homarr-renovate[bot] dea62547e0 fix(deps): update dependency @auth/drizzle-adapter to ^1.6.1 (#1258)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-07 02:16:47 +00:00
homarr-renovate[bot] 1d281c0cc9 fix(deps): update dependency @million/lint to v1.0.9 (#1255)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-06 02:19:40 +00:00
homarr-renovate[bot] 9c5ca6f906 fix(deps): update dependency @auth/drizzle-adapter to ^1.6.0 (#1251)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-05 17:48:50 +00:00
homarr-renovate[bot] d0d2a82a68 fix(deps): update dependency @auth/core to ^0.36.0 (#1250)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-05 17:38:25 +00:00
Meier Lukas 1421ccc917 fix: permissions not restricted for certain management pages / actions (#1219)
* fix: restrict parts of manage navigation to admins

* fix: restrict stats cards on manage home page

* fix: restrict access to amount of certain stats for manage home

* fix: restrict visibility of board create button

* fix: restrict access to integration pages

* fix: restrict access to tools pages for admins

* fix: restrict access to user and group pages

* test: adjust tests to match permission changes for routes

* fix: remove certain pages from spotlight without admin

* fix: app management not restricted
2024-10-05 17:03:32 +02:00
Meier Lukas 770768eb21 fix: rtl common translation unnecessary (#1246)
* fix: rtl common translation unnecessary

* fix: format issue
2024-10-05 16:31:15 +02:00
ManuelandMeier Lukas b14f82b4bb feat: add api keys (#991)
* feat: add api keys

* chore: address pull request feedback

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-10-05 16:18:31 +02:00
homarr-renovate[bot] ee8375756c chore(deps): update node.js to v20.18.0 (#1239)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-05 12:11:37 +02:00
homarr-renovate[bot] 20e6399d5e fix(deps): update dependency winston to v3.15.0 (#1245)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-05 05:30:18 +00:00
oussama Dahmazandoussama Dahmaz b5bfe90453 fix(notebook): default-content image path (#1244)
Co-authored-by: oussama Dahmaz <dahmaz@MacBook-Pro-de-odahmaz.local>
2024-10-04 23:29:32 +02:00
homarr-renovate[bot] 41538a7366 chore(deps): update dependency eslint to ^9.12.0 (#1241)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-04 20:48:44 +00:00
Meier Lukas 0aa760a786 wip: dynamic section menu 2024-10-04 22:47:29 +02:00
Meier Lukas 6ad201f4a2 chore: revert base image to 20.17.0 alpine (#1236) 2024-10-04 21:31:01 +02:00
homarr-renovate[bot] 87b46deaf7 chore(deps): update node.js to v20.18.0 (#1234)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-04 19:09:36 +00:00
Meier Lukas b5e0848436 Merge branch 'dev' into fix-stacked-dynamic-sections-menu 2024-10-04 16:19:13 +02:00
Meier Lukas 4c9471e608 feat(spotlight): add support for custom search-engines (#1200)
* feat(spotlight): add search settings link

* feat(search-engine): add to manage pages

* feat(spotlight): add children option for external search engines

* chore: revert search settings

* fix: deepsource issue

* fix: inconsistent breadcrum placement

* chore: address pull request feedback
2024-10-04 15:59:08 +02:00
homarr-renovate[bot] 8ea8b2ded5 fix(deps): update testcontainers-node monorepo to ^10.13.2 (#1233)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-04 08:34:41 +00:00
Meier Lukas ac0750452c fix: confirm not selected by default in confirm modal (#1232) 2024-10-03 23:24:33 +02:00
homarr-renovate[bot] 547287dba1 fix(deps): update dependency @million/lint to v1.0.8 (#1231)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-03 19:21:39 +00:00
Meier Lukas 665df53a39 fix: new sections without add and change listeners (#1230) 2024-10-03 21:07:57 +02:00
homarr-renovate[bot] 8dde6bb042 chore(deps): update dependency node to v20.18.0 (#1229)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-03 18:36:42 +00:00
Meier Lukas 52b45d835d feat(docker): add encryption key generation for integration secrets (#1202)
* feat(docker): add encryption key generation for integration secrets

* chore: address pull request feedback

* fix: build failing

* fix: docker build fails because of error when encryption key not defined
2024-10-03 19:59:44 +02:00
homarr-renovate[bot] 770a0d63f9 fix(deps): update dependency @million/lint to v1.0.7 (#1225)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-03 17:40:28 +00:00
homarr-renovate[bot] 17572bc602 fix(deps): update dependency tldts to ^6.1.50 (#1228)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-03 17:10:16 +00:00
homarr-renovate[bot] d3836648c2 fix(deps): update mantine monorepo to ^7.13.2 (#1227)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-03 13:22:13 +00:00
homarr-renovate[bot] 9d20362cf1 fix(deps): update dependency eslint-plugin-import to ^2.31.0 (#1226)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-03 11:48:30 +00:00
homarr-renovate[bot] 59ab63c6d8 chore(deps): update vitest monorepo to ^2.1.2 (#1222)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-02 16:52:00 +00:00
homarr-renovate[bot] 3277754290 chore(deps): update dependency @types/react to ^18.3.11 (#1221)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-02 15:48:11 +00:00
homarr-renovate[bot] bc4644609c chore(deps): update pnpm to v9.12.0 (#1220)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-02 13:22:50 +00:00
Meier Lukas 8b5ea3c5d3 fix: email verified check is in a newline in users table (#1218) 2024-10-02 12:00:42 +02:00
Meier Lukas d63f6d270f fix: it was possible to go to the create user page when credentials login disabled (#1217) 2024-10-02 10:41:14 +02:00
homarr-renovate[bot] 2f3619b68d fix(deps): update dependency tldts to ^6.1.49 (#1216)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-02 08:15:41 +00:00
homarr-renovate[bot] 6f5c993510 fix(deps): update nextjs monorepo to ^14.2.14 (#1215)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-01 17:48:46 +00:00
homarr-renovate[bot] c3d54b49a0 fix(deps): update tanstack-query monorepo to ^5.59.0 (#1214)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-01 16:15:12 +00:00
Meier Lukas 61333094df feat: add member sync between groups of ldap and homarr (#1149)
* feat: add member sync between groups of ldap and homarr

* chore: remove temporary console statement

* test: add unit tests for adding and removing ldap group members
2024-10-01 16:46:18 +02:00
homarr-renovate[bot] 4a8ed13a87 fix(deps): update dependency eslint-plugin-react to ^7.37.1 (#1213)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-01 14:32:30 +00:00
homarr-renovate[bot] f73dff5482 chore(deps): update turbo monorepo to ^2.1.3 (#1209)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-01 09:41:15 +00:00
homarr-renovate[bot] b3f6645783 fix(deps): update dependency @million/lint to v1.0.0 (#1212)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-10-01 09:31:22 +00:00
homarr-renovate[bot] 1b0d927e88 fix(deps): update dependency ldapts to v7.2.1 (#1211)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-30 22:48:25 +00:00
Meier Lukas f24c06852e fix: sensitive data can be leaked through query parameters (#1208) 2024-09-30 21:59:21 +02:00
homarr-renovate[bot] 233033f1ce fix(deps): update dependency typescript-eslint to ^8.8.0 (#1207)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-30 17:39:20 +00:00
homarr-renovate[bot] 7dd4d4ec7b fix(deps): update tiptap monorepo to v2.8.0 (#1206)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-30 15:31:35 +00:00
homarr-renovate[bot] 0c24cc1a69 chore(deps): update dependency @vitejs/plugin-react to ^4.3.2 (#1204)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-30 11:25:35 +00:00
Yossi Hillali 0f56dc1ecd feat: OMV integration & health monitoring widget (#1142) 2024-09-30 14:05:13 +03:00
homarr-renovate[bot] 6ce466d38e fix(deps): update mantine monorepo to ^7.13.1 (#1205)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-30 07:48:30 +00:00
Meier Lukas 42a49e6461 fix(spotlight): x button not clickable, help links open in current tab, bad screen usage on mobile (#1199)
* fix: spotlight help links should open in new tab

* fix: x button is not clickable in spotlight

* fix: reduce offset for spotlight, to improve screen usage on mobile
2024-09-28 23:30:09 +02:00
Manuel 08c6303fa6 feat: add healthchecks (#1187) 2024-09-28 23:02:28 +02:00
homarr-renovate[bot] 8aaa4f5856 fix(deps): update dependency @tabler/icons-react to ^3.19.0 (#1197)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-28 04:14:54 +00:00
homarr-renovate[bot] c3e1ce30fa fix(deps): update dependency next-auth to v5.0.0-beta.22 (#1195)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-28 03:48:21 +00:00
homarr-renovate[bot] 9af01bb680 fix(deps): update dependency sass to ^1.79.4 (#1198)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-28 03:35:01 +00:00
homarr-renovate[bot] adb59b292b fix(deps): update dependency @auth/drizzle-adapter to ^1.5.3 (#1194)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-27 20:13:22 +00:00
homarr-renovate[bot] 1994a5938d fix(deps): update dependency @auth/core to ^0.35.3 (#1193)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-27 19:23:18 +00:00
homarr-renovate[bot] f2c3d109cc chore(deps): update dependency @types/react to ^18.3.10 (#1192)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-27 16:50:57 +00:00
homarr-renovate[bot] 3ead13e479 chore(deps): update dependency @types/node to ^20.16.10 (#1191)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-27 16:35:23 +00:00
homarr-renovate[bot] 2245e7aae8 fix(deps): update dependency eslint-plugin-react to ^7.37.0 (#1189)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-27 15:29:22 +00:00
homarr-renovate[bot] f6c7271fb4 fix(deps): update dependency tldts to ^6.1.48 (#1190)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-27 15:11:35 +00:00
homarr-renovate[bot] 1bce7a300d fix(deps): update dependency @tabler/icons-react to ^3.18.0 (#1188)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-26 21:48:08 +00:00
Meier Lukas 8f7ed056c4 fix: issue with color scheme in layout (#1168)
* fix: issue with color scheme in layout

* test: add and adjust unit tests for sign-in-callback
2024-09-26 19:02:45 +02:00
Meier Lukas a30be7bc1e feat: add env variable to disable db migrations on startup (#1179) 2024-09-26 18:54:28 +02:00
homarr-renovate[bot] 2dd21d6688 chore(deps): update dependency @types/node to ^20.16.9 (#1185)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-26 16:52:03 +00:00
homarr-renovate[bot] 4795c8c557 fix(deps): update tiptap monorepo to v2.7.4 (#1186)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-26 13:25:38 +00:00
Meier Lukas 0386449ec1 wip: fix stacked dynamic sections menu 2024-09-25 21:39:56 +02:00
homarr-renovate[bot] 17d6229a0f fix(deps): update dependency @auth/drizzle-adapter to ^1.5.2 (#1181)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 18:36:56 +00:00
Michon van Dooren 65f26117fc fix: container image always being on AMD64 arch (#1173) 2024-09-25 19:56:16 +02:00
homarr-renovate[bot] 80e896f658 chore(deps): update dependency @types/node to ^20.16.8 (#1183)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 17:48:17 +00:00
Michon van Dooren d029f48f79 fix: various issues with docker image workflow (#1172) 2024-09-25 19:34:52 +02:00
homarr-renovate[bot] b2143eddeb fix(deps): update tiptap monorepo to v2.7.3 (#1182)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 16:41:42 +00:00
homarr-renovate[bot] f495383bcf fix(deps): update dependency @auth/core to ^0.35.2 (#1180)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 13:25:00 +00:00
homarr-renovate[bot] 78230b1fe9 fix(deps): update mantine monorepo to ^7.13.0 (#1178)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 09:48:24 +00:00
homarr-renovate[bot] 4e431b4f89 fix(deps): update dependency @auth/drizzle-adapter to ^1.5.1 (#1177)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 09:40:02 +00:00
homarr-renovate[bot] 16e7f938bd fix(deps): update dependency @auth/core to ^0.35.1 (#1176)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 09:30:02 +00:00
homarr-renovate[bot] e2414f5e9f fix(deps): update dependency @tanstack/react-query-devtools to ^5.58.0 (#1175)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 08:17:01 +00:00
homarr-renovate[bot] ea0c35fa6e chore(deps): update dependency @types/node to ^20.16.7 (#1174)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-25 02:15:25 +00:00
SeDemalandMeier Lukas 36915d95fe feat: DnsHole feature parity with oldmarr (#1145)
* feat: DnsHole feature parity with oldmarr
feat: advanced control management
feat: disconnected state
fix: summary widget sizing
feat: summary text flash on update

* feat: dnshole summary integrations disconnected error page

* fix: classnaming

* refactor: small rename, console to logger and unnecessary as conversion changes

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-09-24 23:25:13 +02:00
homarr-renovate[bot] 07f72f7367 chore(deps): update dependency @types/react to ^18.3.9 (#1170)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-24 14:31:29 +00:00
homarr-renovate[bot] 141c65149a chore(deps): update dependency eslint to ^9.11.1 (#1167)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-23 21:34:24 +00:00
homarr-renovate[bot] f3858f0902 chore(deps): update dependency @types/node to ^20.16.6 (#1169)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-23 21:26:23 +00:00
Meier Lukas cc29cb8e43 refactor: replace custom no integration selected logic with existing error (#1166) 2024-09-23 22:44:56 +02:00
homarr-renovate[bot] 6aa5adf5b0 fix(deps): update dependency typescript-eslint to ^8.7.0 (#1165)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-23 17:39:06 +00:00
homarr-renovate[bot] df7ba1edaf chore(deps): update pascalgn/automerge-action action to v0.16.4 (#1164)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-22 20:11:59 +00:00
homarr-renovate[bot] da2c8fa00b chore(deps): update dependency jsdom to ^25.0.1 (#1163)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-22 05:30:19 +00:00
Meier Lukas bbd0ff775a ci: fix issue with pnpm installation (#1160) 2024-09-21 11:36:21 +02:00
Meier Lukas 9eea8d48e8 fix: insert limit of sqlite is smaller than amount of icons (#1161) 2024-09-21 11:08:24 +02:00
homarr-renovate[bot] 612a15810b fix(deps): update dependency sass to ^1.79.3 (#1159)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-20 21:25:49 +00:00
homarr-renovate[bot] 6692c83fbf chore(deps): update dependency eslint to ^9.11.0 (#1157)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-20 20:39:16 +00:00
Manuel 82bfbc6f23 config: ignore build artifacts from docker 2024-09-20 22:26:58 +02:00
Meier Lukas ce1ef3cbe7 feat: add improved search (#1051)
* feat: add improved search

* wip: add support for sorting, rename use-options to use-query-options, add use-options for local usage, add pages search group

* feat: add help links from manage layout to help search mode

* feat: add additional search engines

* feat: add group search details

* refactor: improve users search group type

* feat: add apps search group, add disabled search interaction

* feat: add integrations and boards for search

* wip: hook issue with react

* fix: hook issue regarding actions and interactions

* chore: address pull request feedback

* fix: format issues

* feat: add additional global actions to search

* chore: remove unused code

* fix: search engine short key

* fix: typecheck issues

* fix: deepsource issues

* fix: eslint issue

* fix: lint issues

* fix: unordered dependencies

* chore: address pull request feedback
2024-09-20 16:51:42 +02:00
homarr-renovate[bot] 0c44af2f67 chore(deps): update pnpm to v9.11.0 (#1156)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-20 11:10:56 +00:00
homarr-renovate[bot] dd9080f781 fix(deps): update dependency jotai to ^2.10.0 (#1155)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-20 03:14:53 +00:00
homarr-renovate[bot] a02825113a fix(deps): update dependency sass to ^1.79.2 (#1153)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-20 02:53:49 +00:00
homarr-renovate[bot] 2258dc3ec6 fix(deps): update nextjs monorepo to ^14.2.13 (#1154)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-20 02:11:53 +00:00
homarr-renovate[bot] aa2d440acb fix(deps): update dependency tldts to ^6.1.47 (#1152)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-19 19:48:33 +00:00
homarr-renovate[bot] 2bbde5ebd1 chore(deps): update dependency @types/react to ^18.3.8 (#1151)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-19 19:09:41 +00:00
homarr-renovate[bot] d0e9e0f09d fix(deps): update tiptap monorepo to v2.7.2 (#1150)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-19 09:29:44 +00:00
SeDemal 6d4d6efe3f fix: indexer manager UI behaviour + class naming (#1134) 2024-09-18 12:11:23 +02:00
homarr-renovate[bot] 8f5d7f8387 fix(deps): update nextjs monorepo to ^14.2.12 (#1146)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-18 07:48:58 +00:00
homarr-renovate[bot] 3cb1221038 fix(deps): update dependency sass to ^1.79.1 (#1148)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-18 07:36:26 +00:00
homarr-renovate[bot] f1d5dd03cb fix(deps): update dependency sass to ^1.79.0 (#1147)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-17 23:38:29 +00:00
Meier Lukas fc317840a7 refactor: use suspense query instead of serverdata for app widget (#1143)
* refactor: use suspense query instead of serverdata for app widget

* chore: add missing translation for loading tooltip
2024-09-17 19:30:14 +02:00
Meier Lukas 003cc5160c fix: mysql migration not working (#1138)
* fix: mysql migration not working

* test: add system integration test for mysql migration

* fix: format issues

* fix: deepsource issues

* chore: address bad name for mysql migration test
2024-09-17 19:27:47 +02:00
homarr-renovate[bot] 6f50f37462 chore(deps): update dependency @types/react to ^18.3.7 (#1140)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-17 15:29:18 +00:00
homarr-renovate[bot] 657c2eef80 fix(deps): update tiptap monorepo to v2.7.1 (#1139)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-17 14:39:02 +00:00
Meier Lukas d01ddd61b6 fix: db port validation for env variable wrong (#1137)
* fix: db port validation for env variable wrong

* fix: wrong default
2024-09-16 21:41:40 +02:00
Meier Lukas 6738296830 refactor: move modals to seperate package (#1135)
* refactor: move modals to seperate package

* fix: format issue

* fix: lint issues

* fix: format issue

* fix: only used as type
2024-09-16 19:53:37 +02:00
homarr-renovate[bot] 3ef478c53a fix(deps): update dependency typescript-eslint to ^8.6.0 (#1136)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-16 17:42:28 +00:00
homarr-renovate[bot] 5a73700f57 fix(deps): update dependency next-auth to v5.0.0-beta.21 (#1131)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-16 17:33:37 +00:00
homarr-renovate[bot] 6c39612c07 fix(deps): update dependency mysql2 to v3.11.3 (#1132)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-16 17:11:02 +00:00
homarr-renovate[bot] 5986160265 fix(deps): update dependency @ctrl/transmission to v7 (#1103)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-16 18:45:53 +02:00
homarr-renovate[bot]andMeier Lukas f9cc34c12f fix(deps): update dependency @auth/core to ^0.35.0 (#1129)
* fix(deps): update dependency @auth/core to ^0.35.0

* fix(deps): update dependency @auth/core to ^0.35.0

* fix: type issue

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-09-16 18:42:45 +02:00
SeDemal b9206d3628 refactor: Remove typed-rpc dependency and used fetch instead (#1108)
* feat: Remove typed-rpc dependency and used fetch instead

* fix: tests

* fix: typing
2024-09-16 18:21:27 +02:00
Meier Lukas cd4619845b feat: add nginx proxy (#1073)
* feat: add nginx proxy

* fix: adjust path for websocket client to connect
2024-09-16 18:10:58 +02:00
SeDemal 9fb6e67ed3 feat: Add Simple-Icons and Selfh.st collections (#1118)
* feat: Add Simple-Icons and Selfh.st collections
refactor: icon file path parsing

* refactor: allowed extensions check
2024-09-16 16:34:45 +02:00
homarr-renovate[bot] 929054d5bd fix(deps): update dependency @auth/drizzle-adapter to ^1.5.0 (#1130)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-16 09:48:33 +00:00
homarr-renovate[bot] 414fb32c32 chore(deps): update dependency @types/react to ^18.3.6 (#1133)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-16 09:33:11 +00:00
homarr-renovate[bot] aca5c279b5 fix(deps): update dependency tldts to ^6.1.46 (#1128)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-15 18:34:25 +00:00
homarr-renovate[bot] 6685f4c806 fix(deps): update dependency @tabler/icons-react to ^3.17.0 (#1126)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-15 13:15:44 +00:00
homarr-renovate[bot] 7059a3d88f fix(deps): update dependency tldts to ^6.1.45 (#1121)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-14 09:26:15 +00:00
Manuel 16a5b43695 fix: node cache cleanup error (#1124) 2024-09-14 11:08:09 +02:00
Manuel c8dfbc8e2d fix: workflow inputs and docker push (#1123) 2024-09-14 10:01:22 +02:00
homarr-renovate[bot] 93a3083c60 chore(deps): update vitest monorepo to ^2.1.1 (#1120)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-13 15:48:25 +00:00
homarr-renovate[bot] 7a56006533 fix(deps): update nextjs monorepo to ^14.2.11 (#1117)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 22:48:47 +00:00
homarr-renovate[bot] 50bd4344bc chore(deps): update turbo monorepo to ^2.1.2 (#1119)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 22:40:44 +00:00
homarr-renovate[bot] 2351617177 fix(deps): update dependency eslint-plugin-react to ^7.36.1 (#1115)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 19:09:00 +00:00
homarr-renovate[bot] fb55f1272d fix(deps): update nextjs monorepo to ^14.2.10 (#1109)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 18:50:17 +00:00
homarr-renovate[bot] cae9a800c4 fix(deps): update tanstack-query monorepo to ^5.56.2 (#1116)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 18:36:43 +00:00
homarr-renovate[bot] 2d4681d079 fix(deps): update tanstack-query monorepo to ^5.56.1 (#1114)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 15:12:06 +00:00
homarr-renovate[bot] 564fcea8d7 chore(deps): update vitest monorepo to ^2.1.0 (#1113)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 14:32:45 +00:00
homarr-renovate[bot] 703ecece63 fix(deps): update tanstack-query monorepo to ^5.56.0 (#1112)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 10:33:38 +00:00
homarr-renovate[bot] 41b41a3c53 chore(deps): update dependency concurrently to ^9.0.1 (#1110)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 07:48:18 +00:00
homarr-renovate[bot] cd43253061 fix(deps): update dependency eslint-plugin-react to ^7.36.0 (#1111)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-12 07:34:57 +00:00
Meier Lukas 5e772215a9 fix: hard to move iframe widget #1075 (#1101) 2024-09-11 18:15:47 +02:00
Manuel 2535192b2c feat: downloads widget (#844)
Usenet and Torrent downloads in 1 widget.
sabNZBd, NzbGet, Deluge, qBitTorrent, and transmission support.
Columns can be reordered in Edit mode.
Sorting enabled.
Time uses Dayjs with auto translation.
Can pause/resume single items, clients, or all.
Can delete items (With option to delete assossiated files).
Clients list and details.
Include all filtering and processing for ratio from oldmarr torrent widget.
Invalidation of old data (older than 30 seconds) to show an integration is not responding anymore.

Misc (So many miscs):
Fixed validation error with multiText.
Fixed translation application for multiSelect to behave the same as select.
Added background to gitignore (I needed to add a background to visually test opacity, probably will in the future too)
Added setOptions to frontend components so potential updates made from the Dashboard can be saved.
Extracted background and border color to use in widgets.
humanFileSize function based on the si format (powers of 1024, not 1000).
Improved integrationCreatorByKind by @Meierschlumpf.
Changed integrationCreatorByKind to integrationCreator so it functions directly from the integration.
Added integrationCreatorFromSecrets to directly work with secrets from db.
Added getIntegrationKindsByCategory to get a list of integrations sharing categories.
Added IntegrationKindByCategory type to get the types possible for a category (Great to cast on integration.kind that isn't already properly limited/typed but for which we know the limitation)
Added a common AtLeastOneOf type. Applied to TKind and IntegrationSecretKind[] where it was already being used and Added to the getIntegrationKindsByCategory's output to be more freely used.
Added the Modify type, instead of omiting to then add again just to change a parameters type, use the modify instead. Applied code wide already.
Hook to get list of integration depending on permission level of user. (By @Meierschlumpf)
2024-09-11 17:30:21 +02:00
homarr-renovate[bot] 3d4e607a9d chore(deps): update dependency testcontainers to ^10.13.1 (#1100)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-11 13:18:19 +00:00
homarr-renovate[bot] 8c9bb6e5d5 fix(deps): update dependency video.js to ^8.17.4 (#1099)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-11 09:48:23 +00:00
homarr-renovate[bot] d2a30db3c1 fix(deps): update dependency mysql2 to v3.11.2 (#1098)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-11 03:48:18 +00:00
homarr-renovate[bot] 9d5007c05c fix(deps): update dependency ldapts to v7.2.0 (#1097)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-10 18:35:15 +00:00
homarr-renovate[bot] 18c1da4a62 fix(deps): update dependency @tabler/icons-react to ^3.16.0 (#1096)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-10 16:49:06 +00:00
homarr-renovate[bot] 18347fef3b fix(deps): update dependency tldts to ^6.1.44 (#1095)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-10 12:21:48 +00:00
homarr-renovate[bot] ed0c6136e6 fix(deps): update dependency better-sqlite3 to ^11.3.0 (#1092)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-10 10:48:12 +00:00
homarr-renovate[bot] 40af17083a fix(deps): update dependency mysql2 to v3.11.1 (#1094)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-10 10:32:39 +00:00
homarr-renovate[bot] 3f076fafcd fix(deps): update nextjs monorepo to ^14.2.9 (#1091)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-09 19:21:59 +00:00
homarr-renovate[bot] ea4de527b2 chore(deps): update dependency concurrently to v9 (#1083)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-09 19:52:05 +02:00
homarr-renovate[bot] c844eb2a22 fix(deps): update dependency typescript-eslint to ^8.5.0 (#1089)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-09 17:39:14 +00:00
homarr-renovate[bot] 8069d7acf0 chore(deps): update dependency typescript to ^5.6.2 (#1088)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-09 16:49:18 +00:00
homarr-renovate[bot] 1272976c1a chore(deps): update pnpm to v9.10.0 (#1087)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-09 14:49:10 +00:00
homarr-renovate[bot] 36a7a18c35 fix(deps): update tanstack-query monorepo to ^5.55.4 (#1086)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-09 06:51:43 +00:00
homarr-renovate[bot] 7b6301e498 fix(deps): update dependency @tabler/icons-react to ^3.15.0 (#1085)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-08 21:10:08 +00:00
homarr-renovate[bot] 930bc2d7d2 fix(deps): update tanstack-query monorepo to ^5.55.2 (#1084)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-08 16:33:13 +00:00
homarr-renovate[bot] 6bf0a53d21 fix(deps): update dependency chroma-js to ^3.1.1 (#1082)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-08 08:30:23 +00:00
homarr-renovate[bot] 4f421433c2 fix(deps): update dependency chroma-js to ^3.1.0 (#1081)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-08 07:48:26 +00:00
Yossi HillaliandMeier Lukas 08d4472d8b feat: indexer manager widget (#1057)
* fix(deps): update tanstack-query monorepo to ^5.53.2 (#1055)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>

<br/>
<div align="center">
  <img src="https://homarr.dev/img/logo.png" height="80" alt="" />
  <h3>Homarr</h3>
</div>

**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**

- [ ] Builds without warnings or errors (``pnpm buid``, 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)

* fix: requested changes

* fix: requested changes

* feat: add cron job

* fix: review changes

* fix: add missing oldmarr import mappings

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-09-07 23:18:16 +02:00
Yossi Hillali e88854c0e5 feat: radarr integration (#1053)
* feat: sonarr integration

* fix: reviewed changes
2024-09-07 20:58:40 +02:00
Meier Lukas 5404cebf5b feat: add import for config files from oldmarr (#1019)
* wip: add oldmarr config import

* wip: add support for wrong amount of categories / sections with autofix, color mapping, position adjustments of wrappers

* fix: lockfile broken

* feat: add support for form data trpc requests

* wip: improve file upload

* refactor: restructure import, add import configuration

* wip: add configurations for import to modal

* refactor: move oldmarr import to old-import package

* fix: column count not respects screen size for board

* feat: add beta badge for oldmarr config import

* chore: address pull request feedback

* fix: format issues

* fix: inconsistent versions

* fix: deepsource issues

* fix: revert {} to Record<string, never> convertion to prevent typecheck issue

* fix: inconsistent zod version

* fix: format issue

* chore: address pull request feedback

* fix: wrong import

* fix: broken lock file

* fix: inconsistent versions

* fix: format issues
2024-09-07 18:13:24 +02:00
homarr-renovate[bot] fc1bff2110 fix(deps): update dependency tldts to ^6.1.43 (#1080)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-07 10:48:21 +00:00
ManuelandMeier Lukas 19cd41a8e5 feat: add crawling settings (#959)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-09-06 22:51:18 +02:00
homarr-renovate[bot] d20384dfe0 chore(deps): update dependency eslint to ^9.10.0 (#1078)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-06 20:35:26 +00:00
Meier Lukas 8c205535eb fix: wevbsocket url is always localhost (#1072) 2024-09-06 21:11:58 +02:00
Yossi Hillali 753d091dbb fix: display on safari (#1071) 2024-09-06 16:29:53 +02:00
homarr-renovate[bot] 00d5020824 fix(deps): update tanstack-query monorepo to ^5.55.0 (#1070)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-06 06:36:38 +00:00
homarr-renovate[bot] 247de555ce fix(deps): update dependency tldts to ^6.1.42 (#1069)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-05 16:34:36 +00:00
homarr-renovate[bot] ca83872151 fix(deps): update nextjs monorepo to ^14.2.8 (#1067)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-04 22:29:57 +00:00
homarr-renovate[bot] 049e1107e6 chore(deps): update dependency @types/node to ^20.16.5 (#1068)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-04 22:11:35 +00:00
homarr-renovate[bot] 364d6f4555 chore(deps): update dependency @types/node to ^20.16.4 (#1065)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-04 10:12:07 +00:00
homarr-renovate[bot] bef5832470 fix(deps): update dependency eslint-plugin-jsx-a11y to ^6.10.0 (#1066)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-04 09:48:34 +00:00
homarr-renovate[bot] 3d1b626baa fix(deps): update dependency sass to ^1.78.0 (#1064)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-03 23:27:20 +00:00
homarr-renovate[bot] 9e1faa03b1 fix(deps): update tanstack-query monorepo to ^5.54.1 (#1063)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-03 21:26:08 +00:00
homarr-renovate[bot] 1249fb9612 fix(deps): update dependency eslint-plugin-react to ^7.35.2 (#1062)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-03 19:35:26 +00:00
homarr-renovate[bot] 0f34bf85a8 fix(deps): update dependency eslint-plugin-import to ^2.30.0 (#1061)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-03 07:24:19 +00:00
homarr-renovate[bot] 6ea4a74675 fix(deps): update dependency @tanstack/react-query-devtools to ^5.54.0 (#1060)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-02 23:48:14 +00:00
homarr-renovate[bot] 4c52cc338e fix(deps): update dependency eslint-plugin-react to ^7.35.1 (#1059)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-02 23:35:46 +00:00
homarr-renovate[bot] 1d236678b3 fix(deps): update dependency typescript-eslint to ^8.4.0 (#1058)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-02 17:34:48 +00:00
homarr-renovate[bot] 8fe162e16c fix(deps): update tanstack-query monorepo to ^5.53.3 (#1056)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-02 15:36:14 +00:00
Manuel 0c657276a5 feat: support arm64 (#1022)
* feat: support arm64

* feat: support arm64
2024-09-02 17:26:04 +02:00
homarr-renovate[bot] d69fed6514 fix(deps): update tanstack-query monorepo to ^5.53.2 (#1055)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-01 21:10:14 +00:00
Meier Lukas b080e0de71 feat: add colorscheme to user in db (#987) 2024-09-01 20:37:52 +02:00
homarr-renovate[bot] 824ec8a9ca chore(deps): update dependency @types/node to ^20.16.3 (#1054)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-09-01 14:08:42 +00:00
Yossi HillaliandManuel 6ff36405ba feat: Prowlarr integration (#965)
Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
2024-09-01 15:40:22 +02:00
homarr-renovate[bot] acb4cb9c82 chore(deps): update dependency testcontainers to ^10.13.0 (#1052)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-31 21:09:47 +00:00
homarr-renovate[bot] fcf23c3872 chore(deps): update turbo monorepo to ^2.1.1 (#1050)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-30 21:26:06 +00:00
homarr-renovate[bot] 51be02477b fix(deps): update mantine monorepo to ^7.12.2 (#1048)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-30 14:10:14 +00:00
homarr-renovate[bot] 6d437bd2be chore(deps): update dependency jsdom to v25 (#1030)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-30 14:00:10 +02:00
homarr-renovate[bot] 775c3597dc fix(deps): update tanstack-query monorepo to ^5.53.1 (#1045)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-30 10:41:53 +00:00
homarr-renovate[bot] 0f14e378d7 chore(deps): update dependency @types/react to ^18.3.5 (#1044)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-30 10:31:13 +00:00
homarr-renovate[bot] afb6adf1e6 fix(deps): update tanstack-query monorepo to ^5.52.3 (#1043)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-29 23:25:54 +00:00
Meier Lukas 2738f40c5e fix: when removing one item and then adding another one that newly added one can not be moved (#1025)
* fix: when removing one item and then adding another one that newly added one can not be moved

* chore: address pull request feedback

* fix: deepsource canceled
2024-08-29 22:26:02 +02:00
Thomas CamlongandMeier Lukas 94984ac11b feat: add million-lint (#607)
* feat: Add `million-lint`

* fix: some parts not working because of missing skipTransform flag

* chore: address pull request feedback

* chore: make with-million-lint method that can be used when debugging performance

* fix: format issue

* fix: format issue

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-08-29 22:25:51 +02:00
homarr-renovate[bot] c7a7cef1a2 chore(deps): update dependency @types/node to ^20.16.2 (#1042)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-28 10:11:00 +00:00
Yossi Hillali 69e69ee315 fix: DnsHole controls timer (#953)
* fix: DnsHole controls timer

* fix: use plural naming

* fix: reviewed changes

* fix: change ControlsCard to jsx component

* fix: DeepSource
2024-08-28 11:26:36 +03:00
homarr-renovate[bot] 3771a61c49 fix(deps): update nextjs monorepo to ^14.2.7 (#1041)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-27 16:13:16 +00:00
homarr-renovate[bot] f7f676ace9 chore(deps): update turbo monorepo to ^2.1.0 (#1040)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-27 14:29:27 +00:00
homarr-renovate[bot] 961fca0f64 fix(deps): update dependency @tabler/icons-react to ^3.14.0 (#1039)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-27 10:11:09 +00:00
homarr-renovate[bot] b507bf2ae6 fix(deps): update dependency ldapts to v7.1.1 (#1038)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-26 23:25:34 +00:00
homarr-renovate[bot] 48d01e3c6c fix(deps): update tanstack-query monorepo to ^5.52.2 (#1037)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-26 18:08:40 +00:00
homarr-renovate[bot] b0e269209e fix(deps): update dependency typescript-eslint to ^8.3.0 (#1036)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-26 18:02:58 +00:00
homarr-renovate[bot] 09c88a1c36 chore(deps): update pnpm to v9.9.0 (#1035)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-26 17:10:32 +00:00
homarr-renovate[bot] 2ea0d04c71 fix(deps): update dependency @tabler/icons-react to ^3.13.0 (#1034)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-26 13:36:27 +00:00
homarr-renovate[bot] 78b45c4a5b fix(deps): update dependency drizzle-kit to ^0.24.2 (#1033)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-26 13:17:31 +00:00
homarr-renovate[bot] e60e4a49f8 fix(deps): update dependency trpc-swagger to ^1.2.6 (#1032)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-25 16:40:57 +00:00
homarr-renovate[bot] 6351bf78ff fix(deps): update dependency swagger-ui-react to ^5.17.14 (#1031)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-25 16:30:40 +00:00
Manuel f0cd45c813 feat: implement openapi (#482) 2024-08-25 18:03:32 +02:00
homarr-renovate[bot] a3520e4dfd chore(deps): update dependency jsdom to ^24.1.3 (#1028)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-25 11:33:29 +00:00
homarr-renovate[bot] e193c1c6f5 fix(deps): update dependency @t3-oss/env-nextjs to ^0.11.1 (#1029)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-25 11:23:17 +00:00
homarr-renovate[bot] 89fe289fc5 chore(deps): update dependency jsdom to ^24.1.2 (#1027)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-25 10:29:18 +00:00
Yossi Hillali e34af8659b fix: testConnectionAsync url (#1024) 2024-08-25 12:56:08 +03:00
Meier Lukas 022fad92a9 fix: empty wrappers without items should not be visible in view mode (#1026) 2024-08-25 11:38:05 +02:00
homarr-renovate[bot] ec2de901aa chore(deps): update dependency testcontainers to ^10.12.0 (#1023)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-24 17:27:41 +00:00
Meier Lukas b81f5a2ee3 fix: improve ping performance (#1020) 2024-08-24 16:56:40 +02:00
acbb834889 feat: add media requests widget (#774)
Co-authored-by: SeDemal <Tagaishi@hotmail.ch>
Co-authored-by: SeDemal <demal.sebastien@bluewin.ch>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-08-24 15:23:16 +02:00
homarr-renovate[bot] 7ec4adcb24 chore(deps): update dependency eslint to ^9.9.1 (#1021)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-23 20:48:24 +00:00
homarr-renovate[bot] cbf2ecfb9d fix(deps): update tiptap monorepo to v2.6.6 (#1015)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-23 10:48:40 +00:00
homarr-renovate[bot] 8779b7d607 fix(deps): update tanstack-query monorepo to ^5.52.1 (#1013)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-22 17:09:49 +00:00
homarr-renovate[bot] cd0af7e8f1 fix(deps): update dependency drizzle-kit to ^0.24.1 (#1012)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-22 14:10:03 +00:00
homarr-renovate[bot] 1311e72ca0 fix(deps): update nextjs monorepo to ^14.2.6 (#1009)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-22 11:35:43 +00:00
homarr-renovate[bot] 7742d5f5b4 fix(deps): update dependency tldts to ^6.1.41 (#1011)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-22 11:22:41 +00:00
homarr-renovate[bot] df08258ba1 chore(deps): update pnpm to v9.8.0 (#1010)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-22 11:10:40 +00:00
homarr-renovate[bot] 30ee123072 chore(deps): update node.js to v20.17.0 (#1008)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-21 22:09:56 +00:00
homarr-renovate[bot] 051d57bb39 fix(deps): update dependency better-sqlite3 to ^11.2.1 (#1006)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-21 17:48:19 +00:00
homarr-renovate[bot] f10c8fcb48 chore(deps): update dependency node to v20.17.0 (#1007)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-21 17:34:23 +00:00
homarr-renovate[bot] d401c8fa0f fix(deps): update tiptap monorepo to v2.6.5 (#1005)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-21 07:34:49 +00:00
homarr-renovate[bot] 4e6437a180 fix(deps): update dependency better-sqlite3 to ^11.2.0 (#1004)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-21 06:48:16 +00:00
homarr-renovate[bot] 34374e9a18 chore(deps): update dependency @types/react to ^18.3.4 (#1003)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-20 20:48:42 +00:00
homarr-renovate[bot] 432af79055 fix(deps): update tanstack-query monorepo to ^5.52.0 (#1002)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-20 19:33:58 +00:00
homarr-renovate[bot] 27d08baa2d fix(deps): update dependency dayjs to ^1.11.13 (#1001)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-20 19:21:08 +00:00
homarr-renovate[bot] 72aed22125 fix(deps): update dependency @umami/node to ^0.4.0 (#1000)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-19 23:11:49 +00:00
Meier Lukas 2d155fa0c4 feat: add password requirements (#988)
* feat: add password requirements

* fix: format issue

* fix: unexpected empty string in component jsx

* test: adjust unit test passwords
2024-08-19 21:11:36 +02:00
homarr-renovate[bot] 058518710e fix(deps): update dependency typescript-eslint to ^8.2.0 (#999)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-19 17:48:15 +00:00
homarr-renovate[bot] 9666440e80 fix(deps): update dependency undici to v6.19.8 (#998)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-19 17:36:28 +00:00
homarr-renovate[bot] f5c380f75b fix(deps): update dependency tldts to ^6.1.40 (#996)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-19 13:48:29 +00:00
homarr-renovate[bot] 33441d11ac chore(deps): update dependency @types/node to ^20.16.1 (#995)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-19 13:37:43 +00:00
homarr-renovate[bot] 2f77be8045 fix(deps): update tanstack-query monorepo to ^5.51.24 (#997)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-19 13:30:11 +00:00
homarr-renovate[bot] 0883173d7b chore(deps): update dependency @types/node to ^20.16.0 (#993)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-18 10:29:28 +00:00
Meier Lukas 7521ef0519 feat: add hotkeys (#986) 2024-08-18 12:07:00 +02:00
homarr-renovate[bot] 3eb90968ac fix(deps): update dependency chroma-js to v3 (#992)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-18 11:45:32 +02:00
Meier Lukas c5894136f6 refactor: remove item import for now (#985) 2024-08-16 22:49:56 +02:00
homarr-renovate[bot] 17b29620fd chore(deps): update dependency @types/node to ^20.15.0 (#989)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-16 18:33:10 +00:00
homarr-renovate[bot] 5e7ee1b657 fix(deps): update dependency eslint-config-turbo to ^2.0.14 (#984)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-15 15:10:13 +00:00
homarr-renovate[bot] 5bf4a2d95b chore(deps): update dependency @turbo/gen to ^2.0.14 (#982)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-15 14:48:39 +00:00
homarr-renovate[bot] 1657ebc288 chore(deps): update dependency turbo to ^2.0.14 (#983)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-15 14:36:38 +00:00
homarr-renovate[bot] 0ba647ad51 fix(deps): update tiptap monorepo to v2.6.4 (#981)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-15 09:26:57 +00:00
homarr-renovate[bot] cc61e27fad fix(deps): update tiptap monorepo to v2.6.3 (#980)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-14 19:23:31 +00:00
homarr-renovate[bot] b9bcc1d23a fix(deps): update dependency winston to v3.14.2 (#979)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-14 17:33:59 +00:00
homarr-renovate[bot] 497363b491 fix(deps): update dependency eslint-config-turbo to ^2.0.13 (#978)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-14 16:14:14 +00:00
homarr-renovate[bot] f2629d9ac7 chore(deps): update pnpm to v9.7.1 (#977)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-14 15:48:30 +00:00
homarr-renovate[bot] ced289095c chore(deps): update dependency turbo to ^2.0.13 (#976)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-14 15:34:44 +00:00
homarr-renovate[bot] 38b011a767 chore(deps): update dependency @turbo/gen to ^2.0.13 (#975)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-14 15:27:00 +00:00
homarr-renovate[bot] ba3f1739f4 fix(deps): update dependency @drizzle-team/brocli to ^0.10.1 (#974)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-13 19:34:02 +00:00
homarr-renovate[bot] 81a13b0e59 fix(deps): update tiptap monorepo to v2.6.2 (#972)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-13 16:49:03 +00:00
homarr-renovate[bot] 4a34ec91e5 fix(deps): update tiptap monorepo to v2.6.1 (#970)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-13 14:38:08 +00:00
homarr-renovate[bot] 04725942ee fix(deps): update dependency video.js to ^8.17.3 (#971)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-13 09:28:28 +00:00
homarr-renovate[bot] 9d62fb42c4 fix(deps): update dependency jotai to ^2.9.3 (#969)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-13 03:11:47 +00:00
homarr-renovate[bot] b9f048c57d fix(deps): update dependency typescript-eslint to ^8.1.0 (#968)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-12 17:35:41 +00:00
homarr-renovate[bot] 5b651a782a fix(deps): update mantine monorepo to ^7.12.1 (#967)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-12 13:16:26 +00:00
Meier Lukas f8c9ae67f8 wip: add modal to move items on board (#927) 2024-08-10 22:58:48 +02:00
homarr-renovate[bot] 4b39c16d25 fix(deps): update dependency tldts to ^6.1.39 (#966)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-10 17:34:44 +00:00
Meier Lukas bc886d9476 feat: add collapse to login screen with cli command for password reset (#963)
* feat: add collapse to login screen with cli command for password reset

* refactor: add translations
2024-08-10 15:54:43 +02:00
Meier Lukas 9ce172e78a feat: add dynamic section (#842)
* chore: add parent_section_id and change position to x and y_offset for sqlite section table

* chore: rename existing positions to x_offset and y_offset

* chore: add related mysql migration

* chore: add missing height and width to section table

* fix: missing width and height in migration copy script

* fix: typecheck issues

* fix: test not working caused by unsimilar schemas

* wip: add dynamic section

* refactor: improve structure of gridstack sections

* feat: add rendering of dynamic sections

* feat: add saving of moved sections

* wip: add static row count, restrict min-width and height

* chore: address pull request feedback

* fix: format issues

* fix: size calculation within dynamic sections

* fix: on resize not called when min width or height is reached

* fix: size of items while dragging is to big

* chore: temporarly remove migration files

* chore: readd migrations

* fix: format and deepsource issues

* chore: remove db_dev.sqlite file

* chore: add *.sqlite to .gitignore

* chore: address pull request feedback

* feat: add dynamic section actions for adding and removing them
2024-08-10 12:37:16 +02:00
homarr-renovate[bot] a9d87e4e6b chore(deps): update dependency eslint to ^9.9.0 (#961)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-09 20:48:37 +00:00
homarr-renovate[bot] f6e551807f chore(deps): update dependency @types/node to ^20.14.15 (#958)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-09 18:33:11 +00:00
homarr-renovate[bot] 3be110097b fix(deps): update dependency undici to v6.19.7 (#950)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-09 17:32:51 +00:00
Meier Lukas f327837d82 feat: add user setting for home board (#956) 2024-08-09 19:24:50 +02:00
Meier Lukas 13e09968d9 feat: add logout redirect url (#954)
* feat: add logout redirect url

* fix: format issue
2024-08-09 19:24:07 +02:00
Meier Lukas 67195c6051 fix: wrong id for renovate auto approval (#957) 2024-08-09 19:23:14 +02:00
Meier Lukas fcb72e6716 feat: add column count and is public options to board creation modal (#930)
* feat: add column count and is public options to board creation modal

* test: adjust board creation test to match modified schema
2024-08-09 18:28:52 +02:00
Meier Lukas 349c49462f fix: redirect url from login not working (#952) 2024-08-09 16:08:08 +02:00
Meier Lukas 6dafbaae48 feat: add session expiry (#951) 2024-08-09 15:59:00 +02:00
Meier Lukas 25452ff063 feat: change visibility in item options based on selected integration kinds (#949) 2024-08-09 15:55:30 +02:00
Meier Lukas ab7d3f8e4c fix: renovate approval workflow not running (#948) 2024-08-09 15:25:07 +02:00
homarr-renovate[bot] 21c0e4b354 fix(deps): update dependency @tabler/icons-react to ^3.12.0 (#944)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-09 02:33:55 +00:00
homarr-renovate[bot] e18c773af9 chore(deps): update dependency vite-tsconfig-paths to ^5.0.1 (#946)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-09 01:57:01 +00:00
Meier Lukas c833f2cbf2 fix: github actor name for automatic approval wrong (#943) 2024-08-08 22:27:01 +02:00
homarr-renovate[bot] db9c200f89 fix(deps): update dependency winston to v3.14.1 (#941)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-08 20:26:50 +00:00
Meier Lukas 017c378582 fix: renovate approval run for all pull requests on every pull request change (#942)
* fix: renovate approval run for all pull requests on every pull request change

* fix: run approval only for renovate branches

* fix: restrict actor to renovate bot
2024-08-08 22:02:36 +02:00
homarr-renovate[bot]andMeier Lukas a80b49a03e fix(deps): update dependency @drizzle-team/brocli to ^0.10.0 (#937)
* fix(deps): update dependency @drizzle-team/brocli to ^0.10.0

* fix: rename cliName to name

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-08-08 21:43:04 +02:00
homarr-renovate[bot]andMeier Lukas 013c0c0eeb chore(deps): update dependency vite-tsconfig-paths to v5 (#934)
* chore(deps): update dependency vite-tsconfig-paths to v5

* fix: esm only issue

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-08-08 21:29:52 +02:00
Meier Lukas 9258c397e0 fix: alert that certain fields in user settings are disabled is always shown (#928)
* fix: alert that certain fields in user settings are disabled is always shown

* fix: wrong inverted check
2024-08-08 20:38:17 +02:00
Meier Lukas 81946e50a9 feat: add duplication action for items (#926) 2024-08-08 20:37:41 +02:00
Meier Lukas 365e267b8d fix: ping urls are not reset when restarting (#924) 2024-08-08 20:36:51 +02:00
homarr-renovate[bot] f9e2df085b fix(deps): update tanstack-query monorepo to ^5.51.23 (#936)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-08 17:07:53 +02:00
homarr-renovate[bot] 940a4890e2 fix(deps): update dependency drizzle-kit to ^0.24.0 (#939)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-08 14:48:48 +00:00
homarr-renovate[bot] c399d832e5 fix(deps): update dependency drizzle-orm to ^0.33.0 (#938)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-08 14:36:20 +00:00
homarr-renovate[bot] ac4b4ed9cf fix(deps): update dependency winston to v3.14.0 (#935)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-08 05:29:08 +00:00
homarr-renovate[bot]andMeier Lukas c2bf6779c0 fix(deps): update dependency jotai to ^2.9.2 (#933)
* fix(deps): update dependency jotai to ^2.9.2

* fix: lint issue

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-08-08 07:04:17 +02:00
Yossi Hillali 44712608b7 feat: AdGuard Home integration (#929)
* feat: AdGuard Home integration

* fix: code improvments

* fix: a better errorMessages method
2024-08-07 09:06:59 +03:00
homarr-renovate[bot] d4c2bd2789 chore(deps): update pnpm to v9.7.0 (#932)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-07 01:53:06 +00:00
homarr-renovate[bot] a2be781f81 fix(deps): update dependency tldts to ^6.1.38 (#931)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-06 20:09:19 +00:00
Meier Lukas c4c4d41e4d feat: add support for app url variables (#915)
* feat: add support for app url variables

* fix: test not working

* fix: format issue
2024-08-06 21:43:12 +02:00
homarr-renovate[bot] 693e319e26 chore(deps): update turbo monorepo to ^2.0.12 (#923)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-06 17:37:19 +00:00
homarr-renovate[bot] c6bb272318 fix(deps): update tiptap monorepo to v2.5.9 (#922)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-06 15:36:34 +00:00
homarr-renovate[bot] c69483a12c fix(deps): update dependency typescript-eslint to ^8.0.1 (#921)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-05 17:38:02 +00:00
homarr-renovate[bot] 137ebf6d22 fix(deps): update mantine monorepo to ^7.12.0 (#920)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-05 16:48:06 +00:00
homarr-renovate[bot] 3f35163481 fix(deps): update dependency drizzle-orm to ^0.32.2 (#919)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-05 16:31:47 +00:00
homarr-renovate[bot] d378ddb42b fix(deps): update dependency drizzle-kit to ^0.23.2 (#918)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-05 15:48:23 +00:00
homarr-renovate[bot] 796261c5f5 fix(deps): update dependency video.js to ^8.17.2 (#917)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-05 08:32:53 +00:00
Meier Lukas 7fa5e70d5b feat: reset password cli (#903)
* feat: add password reset cli

* feat: add homarr cli to docker image
2024-08-04 21:44:51 +02:00
Meier Lukas ca1ecdf848 feat: add preview to icon picker (#914) 2024-08-04 21:20:21 +02:00
Meier Lukas b53a2b6b78 feat: add refresh button to docker containers table (#913) 2024-08-04 21:19:31 +02:00
Meier Lukas b8b084c188 fix: restrict access to docker containers page to admins (#912) 2024-08-04 21:19:16 +02:00
SeDemal 0cec1dbb17 fix: Fix typecheck, lint issues and errors brought to dnshole summary. (#916) 2024-08-04 20:46:27 +02:00
Yossi Hillali 65c6854e44 feat: dnshole controls widget (#867)
* feat: dnshole controls widget

* feat: add duration and timer modal

* fix: code improvment

* fix: add support for many integrations

* fix: add support for more integrations

* fix: move ControlsCard outside of main component

* fix: deepsource
2024-08-04 18:47:00 +03:00
Manuel 9013d5dbf0 feat: remove manual flag to pushing image 2024-08-02 21:41:26 +02:00
homarr-renovate[bot] 2a459dda20 fix(deps): update tanstack-query monorepo to ^5.51.21 (#905)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-02 19:21:00 +00:00
homarr-renovate[bot] afe2637f2d chore(deps): update dependency @types/node to ^20.14.14 (#902)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-02 08:48:37 +00:00
homarr-renovate[bot] 26615d80e7 fix(deps): update tanstack-query monorepo to ^5.51.18 (#900)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-01 14:09:47 +00:00
homarr-renovate[bot] 5c73b54428 fix(deps): update tanstack-query monorepo to ^5.51.17 (#899)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-08-01 13:35:52 +00:00
homarr-renovate[bot]andMeier Lukas a9a46024e2 fix(deps): update dependency typescript-eslint to v8 (#896)
* fix(deps): update dependency typescript-eslint to v8

* fix: lint issues

* fix: more lint issues

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-08-01 10:13:12 +02:00
homarr-renovate[bot] 1811e1de79 chore(deps): update turbo monorepo to ^2.0.11 (#897)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-31 21:25:09 +00:00
homarr-renovate[bot] 25e732bf45 chore(deps): update vitest monorepo to ^2.0.5 (#894)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-31 11:22:23 +00:00
homarr-renovate[bot] 5256853351 fix(deps): update dependency undici to v6.19.5 (#893)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-31 10:49:18 +00:00
homarr-renovate[bot] 3a5f818e7c fix(deps): update tanstack-query monorepo to ^5.51.16 (#892)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-31 09:35:52 +00:00
homarr-renovate[bot] 15171dd5af fix(deps): update dependency chroma-js to ^2.6.0 (#891)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-30 23:21:24 +00:00
homarr-renovate[bot] 79cabe6580 fix(deps): update dependency chroma-js to ^2.5.0 (#890)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-30 22:48:25 +00:00
homarr-renovate[bot] 5f89d93db6 fix(deps): update dependency drizzle-kit to ^0.23.1 (#888)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-30 22:33:12 +00:00
homarr-renovate[bot] d98112eae4 chore(deps): update turbo monorepo to ^2.0.10 (#889)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-30 22:24:17 +00:00
homarr-renovate[bot] e0566dfcfa fix(deps): update tiptap monorepo to v2.5.8 (#887)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-30 09:26:56 +00:00
homarr-renovate[bot] 1a24ba6ef7 fix(deps): update dependency typescript-eslint to ^7.18.0 (#886)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-29 17:33:32 +00:00
homarr-renovate[bot] 74f1580cba chore(deps): update dependency testcontainers to ^10.11.0 (#885)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-29 15:48:25 +00:00
homarr-renovate[bot] bce73bc38f fix(deps): update dependency video.js to ^8.17.1 (#884)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-29 09:48:21 +00:00
homarr-renovate[bot] e40d2d776f chore(deps): update dependency @types/ws to ^8.5.12 (#883)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-29 09:11:18 +00:00
Meier Lukas 67dad45214 fix: fetch timeout for external requests to small (#881)
* fix: fetch timeout for external requests to small

* fix: format issue

* fix: move clear timeout for fetch to finally
2024-07-29 06:30:56 +02:00
Meier Lukas 406ed16ce4 fix: overview in series of sonarr schema is optional (#880) 2024-07-28 19:38:47 +02:00
Meier Lukas 9cb6200895 feat: add validation to widget edit modal inputs (#879)
* feat: add validation to widget edit modal inputs

* chore: remove unused console.log statements
2024-07-28 19:38:19 +02:00
homarr-renovate[bot] 851f5e486d chore(deps): update dependency @types/node to ^20.14.13 (#882)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-28 10:28:40 +00:00
homarr-renovate[bot] a0c55459d1 fix(deps): update dependency next-auth to v5.0.0-beta.20 (#878)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-28 04:12:05 +00:00
homarr-renovate[bot] 37309e0727 fix(deps): update dependency @auth/drizzle-adapter to ^1.4.2 (#877)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-28 03:48:18 +00:00
homarr-renovate[bot] 9181f11b13 fix(deps): update dependency @auth/core to ^0.34.2 (#876)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-28 03:27:46 +00:00
Meier Lukas 8e10c48d2f fix: videos not playing automatically on iOS / mobile (#875) 2024-07-27 23:00:45 +02:00
Meier Lukas 55a000a7fc fix: missing breadcrumb translation, wrong spacing in user layout (#872) 2024-07-27 21:09:34 +02:00
Manuel b0e10f0c62 config: test new tag name 2024-07-27 20:04:03 +02:00
Manuel ff847f0910 feat: use different tag name 2024-07-27 19:33:22 +02:00
Manuel fa2c448bac fix: specify step image version 2024-07-27 19:19:48 +02:00
Manuel 4836801004 fix: login before pushing image 2024-07-27 19:17:26 +02:00
Manuel eab5c10fa6 fix: docker push flag 2024-07-27 19:08:18 +02:00
Manuel 1128110be3 fix: docker push flag 2024-07-27 19:02:29 +02:00
Manuel bce085c028 config: add push image input 2024-07-27 18:58:26 +02:00
ManuelandSeDemal 15d9327d46 feat: add rss widget (#760)
Co-authored-by: SeDemal <demal.sebastien@bluewin.ch>
2024-07-27 18:11:29 +02:00
Meier Lukas 4380aa9b3e fix: mantine-react-table not localized #333 (#873) 2024-07-27 17:10:51 +02:00
homarr-renovate[bot] c973a3bfa5 fix(deps): update dependency mysql2 to v3.11.0 (#874)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-27 14:48:10 +00:00
Meier Lukas 6f7327b774 feat: restrict non credential provider interactions (#871)
* wip: add provider field to sqlite user table

* feat: disable invites when credentials provider is not used

* wip: add migration for provider field in user table with sqlite

* wip: remove fields that can not be modified by non credential users

* wip: make username, mail and avatar disabled instead of hidden

* wip: external users membership of group cannot be managed manually

* feat: add alerts to inform about disabled fields and managing group members

* wip: add mysql migration for provider on user table

* chore: fix format issues

* chore: address pull request feedback

* fix: build issue

* fix: deepsource issues

* fix: tests not working

* feat: restrict login to specific auth providers

* chore: address pull request feedback

* fix: deepsource issue
2024-07-27 11:38:51 +02:00
homarr-renovate[bot] eba4052522 chore(deps): update dependency eslint to ^9.8.0 (#870)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-26 21:09:40 +00:00
homarr-renovate[bot] 89ab1df2db fix(deps): update tiptap monorepo to v2.5.7 (#869)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-26 13:13:04 +00:00
homarr-renovate[bot] 63b3c1bff1 fix(deps): update tanstack-query monorepo to ^5.51.15 (#868)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-26 12:50:34 +00:00
homarr-renovate[bot] 04a836e824 fix(deps): update tanstack-query monorepo to ^5.51.14 (#866)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-26 09:26:08 +00:00
Meier Lukas 90ef982066 fix: deployment pipeline for docker not working (#860)
* fix: skip env validation in dockerfile not working

* fix: skip env validation in dockerfile not working

* fix: remove linux/arm/v7 for now as node commands are run forever

* fix: remove cache because it is to big?

* refactor: remove redis log transport during build

* fix: add more checks for conditional redis connection

* fix: docker build not working locally

* refactor: move base image to debian

* chore: add arm v7 platform support

* fix: remove armv7 support as not supported by turbo

* chore: profile amd64 build

* chore: enable webpack logging

* chore: disable linux/arm64 for now

* chore: remove profiling from build in dockerfile

* chore: revert to node alpine image
2024-07-25 19:52:31 +02:00
homarr-renovate[bot] 32a8488b7b chore(deps): update dependency @types/dockerode to ^3.3.31 (#865)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-24 23:10:46 +00:00
homarr-renovate[bot] 69d9a79b05 chore(deps): update node.js to v20.16.0 (#864)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-24 18:13:39 +00:00
homarr-renovate[bot] d70e99c973 fix(deps): update tiptap monorepo to v2.5.6 (#863)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-24 17:48:31 +00:00
homarr-renovate[bot] 20d44121e6 chore(deps): update dependency node to v20.16.0 (#862)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-24 14:10:18 +00:00
homarr-renovate[bot] d8c21f996c chore(deps): update dependency @types/node to ^20.14.12 (#861)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-23 21:14:33 +02:00
homarr-renovate[bot] 868b7b890e fix(deps): update dependency drizzle-orm to ^0.32.1 (#859)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-23 15:11:02 +00:00
homarr-renovate[bot] fc5f02b022 fix(deps): update dependency jotai to ^2.9.1 (#858)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-23 10:10:34 +00:00
homarr-renovate[bot] 47834d4288 chore(deps): update dependency typescript to ^5.5.4 (#857)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 23:24:14 +00:00
Manuel 43e48010e5 fix: job translations (#846) 2024-07-22 21:02:36 +02:00
Manuel 508fcb9861 feat: allow no secrets for integration (#856) 2024-07-22 21:00:14 +02:00
homarr-renovate[bot] b2860ffbd3 fix(deps): update dependency typescript-eslint to ^7.17.0 (#855)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 17:36:50 +00:00
homarr-renovate[bot] 0c7264bc52 fix(deps): update dependency undici to v6.19.4 (#854)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 14:28:46 +00:00
homarr-renovate[bot] c512ddba49 fix(deps): update tiptap monorepo to v2.5.5 (#853)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 11:48:47 +00:00
homarr-renovate[bot] 6a6f9595f6 chore(deps): update vitest monorepo to ^2.0.4 (#852)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 09:39:25 +00:00
homarr-renovate[bot] 1098725bd1 fix(deps): update dependency undici to v6.19.3 (#850)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 09:12:59 +00:00
homarr-renovate[bot] 88e5ac2f71 fix(deps): update dependency @t3-oss/env-nextjs to ^0.11.0 (#851)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 08:49:32 +00:00
homarr-renovate[bot] e5e06baeb3 chore(deps): update pnpm to v9.6.0 (#849)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-22 01:56:52 +00:00
homarr-renovate[bot] 41e576039f fix(deps): update dependency postcss-preset-mantine to ^1.17.0 (#843)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-21 12:16:32 +00:00
homarr-renovate[bot] 56e7aed87f chore(deps): update dependency jsdom to ^24.1.1 (#841)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-21 05:28:47 +00:00
homarr-renovate[bot] c33086d8b5 fix(deps): update dependency ldapts to v7.1.0 (#839)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-20 20:48:07 +00:00
Meier Lukas dc75ffb9e6 feat: add ldap and oidc sso (#500)
* wip: sso

* feat: add ldap client and provider

* feat: implement login form

* feat: finish sso

* fix: lint and format issue

* chore: address pull request feedback

* fix: build not working

* fix: oidc is redirected to internal docker container hostname

* fix: build not working

* refactor: migrate to ldapts

* fix: format and frozen lock file

* fix: deepsource issues

* fix: unit tests for ldap authorization not working

* refactor: remove unnecessary args from dockerfile

* chore: address pull request feedback

* fix: use console instead of logger in auth env.mjs

* fix: default value for auth provider of wrong type

* fix: broken lock file

* fix: format issue
2024-07-20 22:23:58 +02:00
homarr-renovate[bot] 5da74ca7e0 fix(deps): update dependency mantine-react-table to v2.0.0-beta.6 (#837)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-20 13:25:30 +00:00
homarr-renovate[bot] 14d19fcd8b fix(deps): update tanstack-query monorepo to ^5.51.11 (#836)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-20 11:08:48 +00:00
homarr-renovate[bot] 37a8a0fe5a fix(deps): update dependency eslint-plugin-react to ^7.35.0 (#835)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-20 06:14:30 +00:00
homarr-renovate[bot] 445c0c4d2e chore(deps): update turbo monorepo to ^2.0.9 (#834)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-19 22:29:12 +00:00
homarr-renovate[bot] b5379c5352 chore(deps): update turbo monorepo to ^2.0.8 (#833)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-19 15:36:32 +00:00
homarr-renovate[bot] c8f3eb69e5 fix(deps): update tanstack-query monorepo to ^5.51.9 (#831)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-18 13:28:55 +00:00
homarr-renovate[bot] 5c92f6a3cc fix(deps): update dependency dayjs to ^1.11.12 (#832)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-18 13:14:20 +00:00
homarr-renovate[bot] ee2703ca4b fix(deps): update tanstack-query monorepo to ^5.51.8 (#830)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-18 07:24:12 +00:00
homarr-renovate[bot] a4edda6932 fix(deps): update tanstack-query monorepo to ^5.51.5 (#829)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 14:48:25 +00:00
homarr-renovate[bot] 002d011b18 fix(deps): update dependency postcss-preset-mantine to ^1.16.0 (#827)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 13:48:17 +00:00
homarr-renovate[bot] baf552bf52 fix(deps): update tiptap monorepo to v2.5.4 (#828)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 13:35:32 +00:00
homarr-renovate[bot] 015b42aa61 fix(deps): update tiptap monorepo to v2.5.3 (#825)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 12:18:06 +00:00
SeDemal 4e03e7f49d fix: Manage banner animation hitch fix (#826) 2024-07-17 13:45:27 +02:00
homarr-renovate[bot] 86c729a83f fix(deps): update tanstack-query monorepo to ^5.51.4 (#824)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 11:33:49 +00:00
homarr-renovate[bot] 86d526db28 chore(deps): update dependency testcontainers to ^10.10.4 (#823)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 10:11:13 +00:00
homarr-renovate[bot] b82128a90c fix(deps): update tiptap monorepo to v2.5.2 (#822)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-17 08:13:36 +00:00
homarr-renovate[bot] 05bbc19a15 fix(deps): update tanstack-query monorepo to ^5.51.3 (#821)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-16 21:10:26 +00:00
homarr-renovate[bot] 54a4173e72 chore(deps): update turbo monorepo to ^2.0.7 (#820)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-16 20:48:26 +00:00
homarr-renovate[bot] ed1ac628a1 chore(deps): update dependency @types/node to ^20.14.11 (#819)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-16 20:24:28 +00:00
homarr-renovate[bot] 30669d23e3 fix(deps): update dependency @tabler/icons-react to ^3.11.0 (#818)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-16 11:48:26 +00:00
homarr-renovate[bot] c8d57ac3fa chore(deps): update dependency @types/dockerode to ^3.3.30 (#817)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-16 02:30:58 +00:00
homarr-renovate[bot] 636d3c670c fix(deps): update dependency mysql2 to v3.10.3 (#816)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-15 23:10:55 +00:00
homarr-renovate[bot] 56c6be523d fix(deps): update dependency typescript-eslint to ^7.16.1 (#814)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-15 17:35:18 +00:00
homarr-renovate[bot] 08ba282528 fix(deps): update tiptap monorepo to v2.5.1 (#813)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-15 13:29:14 +00:00
homarr-renovate[bot] ea47e4c0a0 chore(deps): update vitest monorepo to ^2.0.3 (#812)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-15 10:30:53 +00:00
homarr-renovate[bot] 7f341efdb3 fix(deps): update tiptap monorepo to v2.5.0 (#811)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-14 14:48:46 +00:00
homarr-renovate[bot] fd642011ba fix(deps): update dependency eslint-plugin-react to ^7.34.4 (#809)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-13 18:29:29 +00:00
homarr-renovate[bot] fe5af9231e fix(deps): update mantine monorepo to ^7.11.2 (#808)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-13 15:33:24 +00:00
Manuel e438bac0f5 feat: make notifications optional 2024-07-13 17:23:08 +02:00
Manuel 5c68ac9e2e fix: try using other variable 2024-07-13 17:22:09 +02:00
Manuel d7a30cd0dd fix: disable env validation in ci 2024-07-13 15:30:29 +02:00
homarr-renovate[bot] 66036b08bc fix(deps): update dependency prettier to ^3.3.3 (#805)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-13 13:25:36 +00:00
Manuel c84fb63b25 fix: disable env validation in ci 2024-07-13 15:09:29 +02:00
Manuel f0d6676917 fix: docker platforms 2024-07-13 15:02:31 +02:00
Manuel f23831be5e fix: add missing tags 2024-07-13 14:54:53 +02:00
Manuel 0c7f479952 fix: use correct branch for tag compare 2024-07-13 14:46:27 +02:00
Manuel a8748fa439 fix: base branch for semver 2024-07-13 14:43:20 +02:00
Manuel 82caec402a feat: use gh app for merging 2024-07-13 14:32:43 +02:00
Manuel 3df6dcfbe7 feat: approve before automerge 2024-07-13 14:19:21 +02:00
Manuel 653f42bccb fix: pass token to cli 2024-07-13 14:06:33 +02:00
Manuel 3fb6458edf refactor: use gh cli to create pr (#795) 2024-07-13 14:00:53 +02:00
homarr-renovate[bot] 8a0411341d chore(deps): update dependency @types/ws to ^8.5.11 (#793)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-13 03:10:57 +00:00
homarr-renovate[bot] 7d61baad03 chore(deps): update dependency eslint to ^9.7.0 (#786)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-12 21:10:47 +00:00
homarr-renovate[bot] 0169501676 fix(deps): update dependency glob to v11 (#766)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-12 20:48:00 +02:00
Manuel 01eb84654d fix: docker build (#777) 2024-07-12 20:46:58 +02:00
Manuel f58c38a1aa fix: correctly set pr description (#778) 2024-07-12 20:46:18 +02:00
homarr-renovate[bot] 0a4ff4b883 fix(deps): update dependency @ianvs/prettier-plugin-sort-imports to ^4.3.1 (#784)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-12 15:26:38 +00:00
homarr-renovate[bot] be4748b8fa fix(deps): update tanstack-query monorepo to ^5.51.1 (#782)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-11 20:34:07 +00:00
homarr-renovate[bot] ec4b3a6408 fix(deps): update dependency sass to ^1.77.8 (#783)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-11 20:25:07 +00:00
homarr-renovate[bot] 5382b1999f fix(deps): update dependency winston to v3.13.1 (#781)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-10 21:33:52 +00:00
homarr-renovate[bot] e54d9aaf44 chore(deps): update vitest monorepo to ^2.0.2 (#780)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-10 16:11:28 +00:00
homarr-renovate[bot] 99175feda4 fix(deps): update nextjs monorepo to ^14.2.5 (#779)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-10 15:27:39 +00:00
homarr-renovate[bot] 3da5734571 fix(deps): update dependency drizzle-orm to ^0.32.0 (#776)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-10 13:28:35 +00:00
homarr-renovate[bot] f2c70a4d0b fix(deps): update dependency drizzle-kit to ^0.23.0 (#775)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-10 13:13:36 +00:00
homarr-renovate[bot] dcb9cfaf29 fix(deps): update dependency sass to ^1.77.7 (#773)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 22:29:07 +00:00
homarr-renovate[bot] 8085e72cf4 fix(deps): update dependency video.js to ^8.16.1 (#772)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 17:09:42 +00:00
homarr-renovate[bot] ea0a6a62a8 chore(deps): update node.js to v20.15.1 (#771)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 16:12:34 +00:00
homarr-renovate[bot] c3f988cc02 chore(deps): update dependency testcontainers to ^10.10.3 (#770)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 14:09:25 +00:00
homarr-renovate[bot] bf70143cca fix(deps): update dependency @tabler/icons-react to ^3.10.0 (#769)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 11:22:21 +00:00
homarr-renovate[bot] 4cee3dfbe0 fix(deps): update dependency glob to ^10.4.5 (#768)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 09:10:29 +00:00
homarr-renovate[bot] 5e3dc1e44d fix(deps): update dependency jotai to ^2.9.0 (#767)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-09 03:37:37 +00:00
homarr-renovate[bot] 74b017d9f4 fix(deps): update dependency glob to ^10.4.4 (#765)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 22:36:03 +00:00
homarr-renovate[bot] 60669999d1 chore(deps): update dependency node to v20.15.1 (#763)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 20:11:45 +00:00
homarr-renovate[bot] ea17975406 chore(deps): update dependency testcontainers to ^10.10.2 (#764)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 19:48:20 +00:00
homarr-renovate[bot] 2c197bc59b fix(deps): update dependency typescript-eslint to ^7.16.0 (#762)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 17:34:19 +00:00
homarr-renovate[bot] 4f94e98140 chore(deps): update vitest monorepo to ^2.0.1 (#761)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 16:11:23 +00:00
Meier Lukas 46943b147a feat: add integration access check to middlewares (#756)
* feat: add integration access check to middlewares

* fix: format issues

* fix: remove group and user permissions and items from context

* refactor: move action check to seperate function
2024-07-08 17:39:36 +02:00
homarr-renovate[bot] 8d42ca8b5e fix(deps): update dependency drizzle-orm to ^0.31.4 (#759)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 15:26:45 +00:00
homarr-renovate[bot] 0d98335354 chore(deps): update vitest monorepo to v2 (#757)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 15:39:35 +02:00
homarr-renovate[bot] 0a3d1e9e5e fix(deps): update dependency drizzle-orm to ^0.31.3 (#758)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-08 13:37:50 +00:00
Meier Lukas 3214d889fa refactor: add color initials to avatar, improve usage of user avatar (#753) 2024-07-08 00:00:54 +02:00
Meier Lukas 408cdeb5c3 feat: add integration access settings (#725)
* feat: add integration access settings

* fix: typecheck and test issues

* fix: test timeout

* chore: address pull request feedback

* chore: add throw if action forbidden for integration permissions

* fix: unable to create new migrations because of duplicate prevId in sqlite snapshots

* chore: add sqlite migration for integration permissions

* test: add unit tests for integration access

* test: add permission checks to integration router tests

* test: add unit test for integration permissions

* chore: add mysql migration

* fix: format issues
2024-07-08 00:00:37 +02:00
Meier Lukas be711149f7 fix: manage links wrong (#754) 2024-07-07 21:32:36 +02:00
homarr-renovate[bot] 680cc284d6 chore(deps): update pnpm to v9.5.0 (#755)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2024-07-07 17:09:25 +00:00
624 changed files with 71493 additions and 7554 deletions

No files matched your search

+2 -1
View File
@@ -5,4 +5,5 @@ npm-debug.log
README.md
.next
.git
dev
dev
.build
-2
View File
@@ -20,8 +20,6 @@ DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE'
# DB_PASSWORD='password'
# DB_NAME='name-of-database'
# @see https://next-auth.js.org/configuration/options#nextauth_url
AUTH_URL='http://localhost:3000'
# You can generate the secret via 'openssl rand -base64 32' on Unix
# @see https://next-auth.js.org/configuration/options#secret
+14 -26
View File
@@ -1,31 +1,19 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"packageRules": [
$schema: "https://docs.renovatebot.com/renovate-schema.json",
extends: ["config:recommended"],
packageRules: [
{
"matchPackagePatterns": [
"^@homarr/",
"tsx" // Disabled for now as version 0.14.4 did not work with the current version of homarr. It resulted in a ERR_MODULE_NOT_FOUND error
],
"enabled": false
matchPackagePatterns: ["^@homarr/"],
enabled: false,
},
{
"matchUpdateTypes": [
"minor",
"patch",
"pin",
"digest"
],
"automerge": true
}
matchUpdateTypes: ["minor", "patch", "pin", "digest"],
automerge: true,
},
],
"updateInternalDeps": true,
"rangeStrategy": "bump",
"automerge": false,
"baseBranches": [
"dev"
],
"dependencyDashboard": false
}
updateInternalDeps: true,
rangeStrategy: "bump",
automerge: false,
baseBranches: ["dev"],
dependencyDashboard: false,
}
+58 -17
View File
@@ -1,18 +1,30 @@
name: "[Deployment] Release"
on:
pull_request:
types:
- closed
push:
branches:
- main
workflow_dispatch: {}
workflow_dispatch:
inputs:
send-notifications:
type: boolean
required: false
default: true
description: Send notifications
push-image:
type: boolean
required: false
default: true
description: Push Docker Image
permissions:
contents: write
packages: write
env:
SKIP_ENV_VALIDATION: true
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
TURBO_TELEMETRY_DISABLED: 1
concurrency: production
@@ -26,19 +38,21 @@ jobs:
node-version: [20]
steps:
- name: Discord notification
if: ${{ github.events.inputs.send-notifications != false }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Deployment of an image has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
args: "Deployment of an image has been triggered: [run ${{ github.run_number }}](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>)"
- uses: actions/checkout@v4
- name: Get Next Version
id: semver
uses: ietf-tools/semver-action@v1
with:
token: ${{ github.token }}
branch: master
branch: dev
- name: Discord notification
if: ${{ github.events.inputs.send-notifications != false }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
@@ -51,17 +65,19 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build artifacts
run: pnpm build
- name: Discord notification
if: ${{ github.events.inputs.send-notifications != false }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Built application artifacts. Building images..."
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
@@ -72,23 +88,48 @@ jobs:
with:
images: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
tags: |
type=raw,value=latest
type=raw,value=${{ steps.semver.outputs.next }}
type=raw,value=alpha
type=raw,value=early-adopters
# tags: |
# type=raw,value=latest
# type=raw,value=${{ steps.semver.outputs.next }}
- name: Build and push
id: buildPushAction
uses: docker/build-push-action@v6
if: ${{ github.events.inputs.push-image == 'true' || github.events.inputs.push-image == null }}
with:
platforms: linux/amd64,linux/arm64,linux/riscv64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64,linux/arm64
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
network: host
env:
SKIP_ENV_VALIDATION: true
- name: Build
id: buildPushDryAction
uses: docker/build-push-action@v6
if: ${{ github.events.inputs.push-image == 'false' }}
with:
platforms: linux/amd64,linux/arm64
context: .
push: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
network: host
env:
SKIP_ENV_VALIDATION: true
- name: Discord notification
if: ${{ github.events.inputs.send-notifications != false && (github.events.inputs.push-image == 'true' || github.events.inputs.push-image == null) }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Deployment of image has completed. Image ID is '${{ steps.buildPushAction.outputs.imageid }}'. This was a dry run."
args: "Deployment of image has completed. Image ID is '${{ steps.buildPushAction.outputs.imageid }}'."
- name: Discord notification
if: ${{ github.events.inputs.send-notifications != false && !(github.events.inputs.push-image == 'true' || github.events.inputs.push-image == null) }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Deployment of image has completed. Image ID is '${{ steps.buildPushDryAction.outputs.imageid }}'. This was a dry run."
+33 -14
View File
@@ -4,6 +4,12 @@ on:
schedule:
- cron: "0 19 * * 5" # https://crontab.guru/#0_19_*_*_5
workflow_dispatch:
inputs:
send-notifications:
type: boolean
required: false
default: true
description: Send notifications
permissions:
contents: write
@@ -14,6 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Discord notification
if: ${{ github.events.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
@@ -30,27 +37,39 @@ jobs:
token: ${{ github.token }}
branch: dev
- name: Create pull request
uses: devops-infra/action-pull-request@v0.5.5
id: create-pull-request
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
title: "chore(release): automatic release ${{ steps.semver.outputs.next }}"
new_string: "**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``"
source_branch: dev
target_branch: main
label: automerge
run: "gh pr create --title \"chore(release): automatic release ${{ steps.semver.outputs.next }}\" --body \"**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``\" --base main --head dev --label automerge"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Discord notification
if: ${{ github.events.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
- name: Obtain token
id: obtainApprovalToken
uses: tibdex/github-app-token@v2
with:
private_key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
app_id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
- name: Approve PR
env:
GITHUB_TOKEN: ${{ steps.obtainApprovalToken.outputs.token }}
run: |
gh pr review --approve --body "Automatically approved by GitHub Action"
- name: Obtain token
id: obtainMergeToken
uses: tibdex/github-app-token@v2
with:
private_key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app_id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
- id: automerge
if: ${{ steps.semver.outputs.bump != 'major' }}
name: automerge
uses: "pascalgn/automerge-action@v0.16.3"
uses: "pascalgn/automerge-action@v0.16.4"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GITHUB_TOKEN: ${{ steps.obtainMergeToken.outputs.token }}
MERGE_METHOD: merge # we prefer merge commits for merging to master
MERGE_COMMIT_MESSAGE: "chore(release): automatic release ${{ steps.semver.outputs.next }}"
MERGE_DELETE_BRANCH: false # never set to true!
@@ -60,21 +79,21 @@ jobs:
MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
- name: Merged Discord notification
if: ${{ steps.automerge.outputs.mergeResult == 'merged' }}
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.events.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Merged PR ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
- name: Major Bump Discord notification
if: ${{ steps.semver.outputs.bump == 'major' }}
if: ${{ steps.semver.outputs.bump == 'major' && github.events.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "The release PR must be manually merged because the next version is a major version: ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
- name: Discord Fail Notification
if: failure()
if: failure() && github.events.inputs.send-notifications
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
@@ -6,6 +6,7 @@ on:
jobs:
approve-renovate-prs:
runs-on: ubuntu-latest
if: github.actor_id == 158783068 # Id of renovate bot see https://api.github.com/users/homarr-renovate%5Bbot%5D
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -21,6 +22,4 @@ jobs:
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
for pr in $(gh pr list --author homarr-renovate[bot] --json number --jq .[].number); do
gh pr review $pr --approve --body "Automatically approved by GitHub Action"
done
gh pr review ${{github.event.pull_request.number}} --approve --body "Automatically approved by GitHub Action"
+8 -2
View File
@@ -48,10 +48,16 @@ yarn-error.log*
.turbo
# database
db.sqlite
*.sqlite
# logs
*.log
apps/tasks/tasks.cjs
apps/websocket/wssServer.cjs
apps/websocket/wssServer.cjs
apps/nextjs/.million/
packages/cli/cli.cjs
#personal backgrounds
apps/nextjs/public/images/background.png
+1 -1
View File
@@ -1 +1 @@
20.15.0
20.18.0
+2 -1
View File
@@ -2,6 +2,7 @@
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"yoavbls.pretty-ts-errors"
"yoavbls.pretty-ts-errors",
"million.million-lint"
]
}
+11 -2
View File
@@ -9,13 +9,22 @@
"js/ts.implicitProjectConfig.experimentalDecorators": true,
"prettier.configPath": "./tooling/prettier/index.mjs",
"cSpell.words": [
"ajnart",
"cqmin",
"gridstack",
"homarr",
"jellyfin",
"mantine",
"manuel-rw",
"Meierschlumpf",
"overseerr",
"Sabnzbd",
"SeDemal",
"Sonarr",
"superjson",
"tabler",
"trpc",
"Umami",
"Sonarr"
"Umami"
],
"i18n-ally.dirStructure": "auto",
"i18n-ally.enabledFrameworks": ["next-international"],
+41 -15
View File
@@ -1,8 +1,9 @@
FROM node:20.15.0-alpine AS base
FROM node:20.18.0-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
RUN apk update
# Set working directory
WORKDIR /app
COPY . .
@@ -11,6 +12,7 @@ RUN turbo prune @homarr/nextjs --docker --out-dir ./next-out
RUN turbo prune @homarr/tasks --docker --out-dir ./tasks-out
RUN turbo prune @homarr/websocket --docker --out-dir ./websocket-out
RUN turbo prune @homarr/db --docker --out-dir ./migration-out
RUN turbo prune @homarr/cli --docker --out-dir ./cli-out
# Add lockfile and package.json's of isolated subworkspace
FROM base AS installer
@@ -22,20 +24,19 @@ WORKDIR /app
COPY .gitignore .gitignore
COPY --from=builder /app/tasks-out/json/ .
COPY --from=builder /app/tasks-out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm && pnpm install
COPY --from=builder /app/websocket-out/json/ .
COPY --from=builder /app/websocket-out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm && pnpm install
COPY --from=builder /app/migration-out/json/ .
COPY --from=builder /app/migration-out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm && pnpm install
COPY --from=builder /app/cli-out/json/ .
COPY --from=builder /app/next-out/json/ .
COPY --from=builder /app/next-out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=builder /app/pnpm-lock.yaml ./pnpm-lock.yaml
# Is used for postinstall of docs definitions
COPY --from=builder /app/packages/definitions/src/docs ./packages/definitions/src/docs
# Uses the lockfile to install the dependencies
RUN corepack enable pnpm && pnpm install --recursive --frozen-lockfile
# Install sharp for image optimization
RUN corepack enable pnpm && pnpm install sharp -w
# Build the project
@@ -43,24 +44,45 @@ COPY --from=builder /app/tasks-out/full/ .
COPY --from=builder /app/websocket-out/full/ .
COPY --from=builder /app/next-out/full/ .
COPY --from=builder /app/migration-out/full/ .
COPY --from=builder /app/cli-out/full/ .
# Copy static data as it is not part of the build
COPY static-data ./static-data
ARG SKIP_ENV_VALIDATION=true
RUN corepack enable pnpm && pnpm turbo run build
ARG SKIP_ENV_VALIDATION='true'
ARG CI='true'
ARG DISABLE_REDIS_LOGS='true'
RUN corepack enable pnpm && pnpm build
FROM base AS runner
WORKDIR /app
RUN apk add --no-cache redis
# gettext is required for envsubst
RUN apk add --no-cache redis nginx bash gettext
RUN mkdir /appdata
RUN mkdir /appdata/db
RUN mkdir /appdata/redis
VOLUME /appdata
RUN mkdir /secrets
VOLUME /secrets
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Enable homarr cli
COPY --from=installer --chown=nextjs:nodejs /app/packages/cli/cli.cjs /app/apps/cli/cli.cjs
RUN echo $'#!/bin/bash\ncd /app/apps/cli && node ./cli.cjs "$@"' > /usr/bin/homarr
RUN chmod +x /usr/bin/homarr
# Don't run production as root
RUN chown -R nextjs:nodejs /appdata
RUN chown -R nextjs:nodejs /secrets
RUN mkdir -p /var/cache/nginx && chown -R nextjs:nodejs /var/cache/nginx && \
mkdir -p /var/log/nginx && chown -R nextjs:nodejs /var/log/nginx && \
mkdir -p /var/lib/nginx && chown -R nextjs:nodejs /var/lib/nginx && \
touch /run/nginx/nginx.pid && chown -R nextjs:nodejs /run/nginx/nginx.pid && \
mkdir -p /etc/nginx/templates /etc/nginx/ssl/certs && chown -R nextjs:nodejs /etc/nginx
USER nextjs
COPY --from=installer /app/apps/nextjs/next.config.mjs .
@@ -78,10 +100,14 @@ COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/.next/standalone ./
COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/.next/static ./apps/nextjs/.next/static
COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/public ./apps/nextjs/public
COPY --chown=nextjs:nodejs scripts/run.sh ./run.sh
COPY --chown=nextjs:nodejs scripts/generateEncryptionKey.js ./generateEncryptionKey.js
COPY --chown=nextjs:nodejs packages/redis/redis.conf /app/redis.conf
COPY --chown=nextjs:nodejs nginx.conf /etc/nginx/templates/nginx.conf
ENV DB_URL='/appdata/db/db.sqlite'
ENV DB_DIALECT='sqlite'
ENV DB_DRIVER='better-sqlite3'
ENV AUTH_PROVIDERS='credentials'
CMD ["sh", "run.sh"]
+14 -5
View File
@@ -1,5 +1,8 @@
// Importing env files here to validate on build
import "@homarr/auth/env.mjs";
import MillionLint from "@million/lint";
import "./src/env.mjs";
/** @type {import("next").NextConfig} */
@@ -9,11 +12,14 @@ const config = {
/** We already do linting and typechecking as separate tasks in CI */
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
webpack: (config) => {
config.module.rules.push({
test: /\.node$/,
loader: "node-loader",
});
webpack: (config, { isServer }) => {
if (isServer) {
config.module.rules.push({
test: /\.node$/,
loader: "node-loader",
});
}
return config;
},
experimental: {
@@ -25,4 +31,7 @@ const config = {
},
};
// Skip transform is used because of webpack loader, without it for example 'Tooltip.Floating' will not work and show an error
const withMillionLint = MillionLint.next({ rsc: true, skipTransform: true, telemetry: false });
export default config;
+38 -32
View File
@@ -7,12 +7,13 @@
"build": "pnpm with-env next build",
"clean": "git clean -xdf .next .turbo node_modules",
"dev": "pnpm with-env next dev",
"lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"start": "pnpm with-env next start",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env --"
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/analytics": "workspace:^0.1.0",
"@homarr/api": "workspace:^0.1.0",
@@ -22,27 +23,31 @@
"@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/form": "workspace:^0.1.0",
"@homarr/gridstack": "^1.0.0",
"@homarr/gridstack": "^1.0.3",
"@homarr/integrations": "workspace:^0.1.0",
"@homarr/log": "workspace:^",
"@homarr/modals": "workspace:^0.1.0",
"@homarr/modals-collection": "workspace:^0.1.0",
"@homarr/notifications": "workspace:^0.1.0",
"@homarr/old-schema": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/spotlight": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"@mantine/colors-generator": "^7.11.1",
"@mantine/core": "^7.11.1",
"@mantine/hooks": "^7.11.1",
"@mantine/modals": "^7.11.1",
"@mantine/tiptap": "^7.11.1",
"@homarr/server-settings": "workspace:^0.1.0",
"@t3-oss/env-nextjs": "^0.10.1",
"@tanstack/react-query": "^5.50.1",
"@tanstack/react-query-devtools": "^5.50.1",
"@tanstack/react-query-next-experimental": "5.50.1",
"@tabler/icons-react": "^3.9.0",
"@mantine/colors-generator": "^7.13.4",
"@mantine/core": "^7.13.4",
"@mantine/hooks": "^7.13.4",
"@mantine/modals": "^7.13.4",
"@mantine/tiptap": "^7.13.4",
"@million/lint": "1.0.11",
"@t3-oss/env-nextjs": "^0.11.1",
"@tabler/icons-react": "^3.20.0",
"@tanstack/react-query": "^5.59.16",
"@tanstack/react-query-devtools": "^5.59.16",
"@tanstack/react-query-next-experimental": "5.59.16",
"@trpc/client": "next",
"@trpc/next": "next",
"@trpc/react-query": "next",
@@ -50,23 +55,24 @@
"@xterm/addon-canvas": "^0.7.0",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "^5.5.0",
"chroma-js": "^2.4.2",
"chroma-js": "^3.1.2",
"clsx": "^2.1.1",
"dayjs": "^1.11.11",
"dayjs": "^1.11.13",
"dotenv": "^16.4.5",
"flag-icons": "^7.2.3",
"glob": "^10.4.3",
"jotai": "^2.8.4",
"mantine-react-table": "2.0.0-beta.5",
"next": "^14.2.4",
"postcss-preset-mantine": "^1.15.0",
"glob": "^11.0.0",
"jotai": "^2.10.1",
"mantine-react-table": "2.0.0-beta.7",
"next": "^14.2.16",
"postcss-preset-mantine": "^1.17.0",
"prismjs": "^1.29.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
"react-error-boundary": "^4.1.2",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.77.6",
"sass": "^1.80.4",
"superjson": "2.2.1",
"swagger-ui-react": "^5.17.14",
"use-deep-compare-effect": "^1.8.1"
},
"devDependencies": {
@@ -74,15 +80,15 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/chroma-js": "2.4.4",
"@types/node": "^20.14.10",
"@types/prismjs": "^1.26.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"concurrently": "^8.2.2",
"eslint": "^9.6.0",
"@types/node": "^20.17.1",
"@types/prismjs": "^1.26.5",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/swagger-ui-react": "^4.18.3",
"concurrently": "^9.0.1",
"eslint": "^9.13.0",
"node-loader": "^2.0.0",
"prettier": "^3.3.2",
"typescript": "^5.5.3"
},
"prettier": "@homarr/prettier-config"
"prettier": "^3.3.3",
"typescript": "^5.6.3"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,74 @@
"use client";
import type { PropsWithChildren } from "react";
import type { MantineColorScheme, MantineColorSchemeManager } from "@mantine/core";
import { createTheme, DirectionProvider, MantineProvider } from "@mantine/core";
import dayjs from "dayjs";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { parseCookies, setClientCookie } from "@homarr/common";
export const CustomMantineProvider = ({ children }: PropsWithChildren) => {
const manager = useColorSchemeManager();
return (
<DirectionProvider>
<MantineProvider
defaultColorScheme="dark"
colorSchemeManager={manager}
theme={createTheme({
primaryColor: "red",
autoContrast: true,
})}
>
{children}
</MantineProvider>
</DirectionProvider>
);
};
function useColorSchemeManager(): MantineColorSchemeManager {
const key = "homarr-color-scheme";
const { data: session } = useSession();
const updateCookieValue = (value: Exclude<MantineColorScheme, "auto">) => {
setClientCookie(key, value, { expires: dayjs().add(1, "year").toDate(), path: "/" });
};
const { mutate: mutateColorScheme } = clientApi.user.changeColorScheme.useMutation({
onSuccess: (_, variables) => {
updateCookieValue(variables.colorScheme);
},
});
return {
get: (defaultValue) => {
if (typeof window === "undefined") {
return defaultValue;
}
try {
const cookies = parseCookies(document.cookie);
return (cookies[key] as MantineColorScheme | undefined) ?? defaultValue;
} catch {
return defaultValue;
}
},
set: (value) => {
if (value === "auto") return;
try {
if (session) {
mutateColorScheme({ colorScheme: value });
}
updateCookieValue(value);
} catch (error) {
console.warn("[@mantine/core] Color scheme manager was unable to save color scheme.", error);
}
},
subscribe: () => undefined,
unsubscribe: () => undefined,
clear: () => undefined,
};
}
@@ -1,14 +1,46 @@
"use client";
import { createContext, useContext, useEffect } from "react";
import type { PropsWithChildren } from "react";
import dayjs from "dayjs";
import type { Session } from "@homarr/auth";
import { SessionProvider } from "@homarr/auth/client";
import { SessionProvider, signIn } from "@homarr/auth/client";
interface AuthProviderProps {
interface AuthProviderProps extends AuthContextProps {
session: Session | null;
}
export const AuthProvider = ({ children, session }: PropsWithChildren<AuthProviderProps>) => {
return <SessionProvider session={session}>{children}</SessionProvider>;
export const AuthProvider = ({ children, session, logoutUrl }: PropsWithChildren<AuthProviderProps>) => {
useLoginRedirectOnSessionExpiry(session);
return (
<SessionProvider session={session}>
<AuthContext.Provider value={{ logoutUrl }}>{children}</AuthContext.Provider>
</SessionProvider>
);
};
interface AuthContextProps {
logoutUrl: string | undefined;
}
const AuthContext = createContext<AuthContextProps | null>(null);
export const useAuthContext = () => {
const context = useContext(AuthContext);
if (!context) throw new Error("useAuthContext must be used within an AuthProvider");
return context;
};
const useLoginRedirectOnSessionExpiry = (session: Session | null) => {
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
if (!session) return () => {};
//setTimeout doesn't allow for a number higher than 2147483647 (2³¹-1 , or roughly 24 days)
const timeout = setTimeout(() => void signIn(), Math.min(dayjs(session.expires).diff(), 2147483647));
return () => clearTimeout(timeout);
}, [session]);
};
@@ -5,14 +5,37 @@ import { useState } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental";
import { createWSClient, loggerLink, unstable_httpBatchStreamLink, wsLink } from "@trpc/client";
import {
createWSClient,
httpLink,
isNonJsonSerializable,
loggerLink,
splitLink,
unstable_httpBatchStreamLink,
wsLink,
} from "@trpc/client";
import superjson from "superjson";
import type { AppRouter } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { env } from "~/env.mjs";
const constructWebsocketUrl = () => {
const fallback = "ws://localhost:3001/websockets";
if (typeof window === "undefined") {
return fallback;
}
if (env.NODE_ENV === "development") {
return fallback;
}
return `ws://${window.location.hostname}:${window.location.port}/websockets`;
};
const wsClient = createWSClient({
url: "ws://localhost:3001",
url: constructWebsocketUrl(),
});
export function TRPCReactProvider(props: PropsWithChildren) {
@@ -34,18 +57,29 @@ export function TRPCReactProvider(props: PropsWithChildren) {
enabled: (opts) =>
process.env.NODE_ENV === "development" || (opts.direction === "down" && opts.result instanceof Error),
}),
(args) => {
return ({ op, next }) => {
console.log("op", op.type, op.input, op.path, op.id);
if (op.type === "subscription") {
const link = wsLink<AppRouter>({
client: wsClient,
transformer: superjson,
});
return link(args)({ op, next });
}
return unstable_httpBatchStreamLink({
splitLink({
condition: ({ type }) => type === "subscription",
true: wsLink<AppRouter>({
client: wsClient,
transformer: superjson,
}),
false: splitLink({
condition: ({ input }) => isNonJsonSerializable(input),
true: httpLink({
/**
* We don't want to transform the data here as we want to use form data
*/
transformer: {
serialize(object: unknown) {
return object;
},
deserialize(data: unknown) {
return data;
},
},
url: `${getBaseUrl()}/api/trpc`,
}),
false: unstable_httpBatchStreamLink({
transformer: superjson,
url: `${getBaseUrl()}/api/trpc`,
headers() {
@@ -53,9 +87,9 @@ export function TRPCReactProvider(props: PropsWithChildren) {
headers.set("x-trpc-source", "nextjs-react");
return headers;
},
})(args)({ op, next });
};
},
}),
}),
}),
],
});
});
@@ -7,6 +7,7 @@ import { clientApi } from "@homarr/api/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { CustomPasswordInput } from "@homarr/ui";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
@@ -64,7 +65,8 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} autoComplete="off" {...form.getInputProps("username")} />
<PasswordInput
<CustomPasswordInput
withPasswordRequirements
label={t("field.password.label")}
autoComplete="new-password"
{...form.getInputProps("password")}
@@ -2,6 +2,7 @@ import { notFound } from "next/navigation";
import { Card, Center, Stack, Text, Title } from "@mantine/core";
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { and, db, eq } from "@homarr/db";
import { invites } from "@homarr/db/schema/sqlite";
import { getScopedI18n } from "@homarr/translation/server";
@@ -19,6 +20,8 @@ interface InviteUsagePageProps {
}
export default async function InviteUsagePage({ params, searchParams }: InviteUsagePageProps) {
if (!isProviderEnabled("credentials")) notFound();
const session = await auth();
if (session) notFound();
@@ -1,78 +1,185 @@
"use client";
import { useState } from "react";
import type { PropsWithChildren } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { Alert, Button, PasswordInput, rem, Stack, TextInput } from "@mantine/core";
import { IconAlertTriangle } from "@tabler/icons-react";
import { Anchor, Button, Card, Code, Collapse, Divider, PasswordInput, Stack, Text, TextInput } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { signIn } from "@homarr/auth/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import type { useForm } from "@homarr/form";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
import { validation, z } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface LoginFormProps {
providers: string[];
oidcClientName: string;
isOidcAutoLoginEnabled: boolean;
callbackUrl: string;
}
export const LoginForm = () => {
const extendedValidation = validation.user.signIn.extend({ provider: z.enum(["credentials", "ldap"]) });
export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, callbackUrl }: LoginFormProps) => {
const t = useScopedI18n("user");
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string>();
const form = useZodForm(validation.user.signIn, {
const [isPending, setIsPending] = useState(false);
const form = useZodForm(extendedValidation, {
initialValues: {
name: "",
password: "",
provider: "credentials",
},
});
const handleSubmitAsync = async (values: z.infer<typeof validation.user.signIn>) => {
setIsLoading(true);
setError(undefined);
await signIn("credentials", {
...values,
redirect: false,
callbackUrl: "/",
})
.then(async (response) => {
if (!response?.ok || response.error) {
throw response?.error;
}
const credentialInputsVisible = providers.includes("credentials") || providers.includes("ldap");
showSuccessNotification({
title: t("action.login.notification.success.title"),
message: t("action.login.notification.success.message"),
});
await revalidatePathActionAsync("/");
router.push("/");
})
.catch((error: Error | string) => {
setIsLoading(false);
setError(error.toString());
showErrorNotification({
title: t("action.login.notification.error.title"),
message: t("action.login.notification.error.message"),
});
const onSuccess = useCallback(
async (response: Awaited<ReturnType<typeof signIn>>) => {
if (response && (!response.ok || response.error)) {
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw response.error;
}
showSuccessNotification({
title: t("action.login.notification.success.title"),
message: t("action.login.notification.success.message"),
});
};
// Redirect to the callback URL if the response is defined and comes from a credentials provider (ldap or credentials). oidc is redirected automatically.
if (response) {
await revalidatePathActionAsync("/");
router.push(callbackUrl);
}
},
[t, router, callbackUrl],
);
const onError = useCallback(() => {
setIsPending(false);
showErrorNotification({
title: t("action.login.notification.error.title"),
message: t("action.login.notification.error.message"),
autoClose: 10000,
});
}, [t]);
const signInAsync = useCallback(
async (provider: string, options?: Parameters<typeof signIn>[1]) => {
setIsPending(true);
await signIn(provider, {
...options,
redirect: false,
callbackUrl: new URL(callbackUrl, window.location.href).href,
})
.then(onSuccess)
.catch(onError);
},
[setIsPending, onSuccess, onError, callbackUrl],
);
const isLoginInProgress = useRef(false);
useEffect(() => {
if (isOidcAutoLoginEnabled && !isPending && !isLoginInProgress.current) {
isLoginInProgress.current = true;
void signInAsync("oidc");
}
}, [signInAsync, isOidcAutoLoginEnabled, isPending]);
return (
<Stack gap="xl">
<form onSubmit={form.onSubmit((values) => void handleSubmitAsync(values))}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} {...form.getInputProps("name")} />
<PasswordInput label={t("field.password.label")} {...form.getInputProps("password")} />
<Button type="submit" fullWidth loading={isLoading}>
{t("action.login.label")}
</Button>
</Stack>
</form>
<Stack gap="lg">
{credentialInputsVisible && (
<>
<form onSubmit={form.onSubmit((credentials) => void signInAsync(credentials.provider, credentials))}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} {...form.getInputProps("name")} />
<PasswordInput label={t("field.password.label")} {...form.getInputProps("password")} />
{error && (
<Alert icon={<IconAlertTriangle size={rem(16)} />} color="red">
{error}
</Alert>
)}
{providers.includes("credentials") && (
<Stack gap="sm">
<SubmitButton isPending={isPending} form={form} provider="credentials">
{t("action.login.label")}
</SubmitButton>
<PasswordForgottenCollapse username={form.values.name} />
</Stack>
)}
{providers.includes("ldap") && (
<SubmitButton isPending={isPending} form={form} provider="ldap">
{t("action.login.labelWith", { provider: "LDAP" })}
</SubmitButton>
)}
</Stack>
</form>
{providers.includes("oidc") && <Divider label="OIDC" labelPosition="center" />}
</>
)}
{providers.includes("oidc") && (
<Button fullWidth variant="light" onClick={async () => await signInAsync("oidc")}>
{t("action.login.labelWith", { provider: oidcClientName })}
</Button>
)}
</Stack>
</Stack>
);
};
interface SubmitButtonProps {
isPending: boolean;
form: ReturnType<typeof useForm<FormType, (values: FormType) => FormType>>;
provider: "credentials" | "ldap";
}
const SubmitButton = ({ isPending, form, provider, children }: PropsWithChildren<SubmitButtonProps>) => {
const isCurrentProviderActive = form.getValues().provider === provider;
return (
<Button
type="submit"
name={provider}
fullWidth
onClick={() => form.setFieldValue("provider", provider)}
loading={isPending && isCurrentProviderActive}
disabled={isPending && !isCurrentProviderActive}
>
{children}
</Button>
);
};
interface PasswordForgottenCollapseProps {
username: string;
}
const PasswordForgottenCollapse = ({ username }: PasswordForgottenCollapseProps) => {
const [visible, { toggle }] = useDisclosure(false);
const tForgotPassword = useScopedI18n("user.action.login.forgotPassword");
const commandUsername = username.trim().length >= 1 ? username.trim() : "<username>";
return (
<>
<Anchor type="button" component="button" onClick={toggle}>
{tForgotPassword("label")}
</Anchor>
<Collapse in={visible}>
<Card>
<Stack gap="xs">
<Text size="sm">{tForgotPassword("description")}</Text>
<Code>homarr reset-password -u {commandUsername}</Code>
</Stack>
</Card>
</Collapse>
</>
);
};
type FormType = z.infer<typeof extendedValidation>;
@@ -1,11 +1,26 @@
import { redirect } from "next/navigation";
import { Card, Center, Stack, Text, Title } from "@mantine/core";
import { env } from "@homarr/auth/env.mjs";
import { auth } from "@homarr/auth/next";
import { getScopedI18n } from "@homarr/translation/server";
import { HomarrLogoWithTitle } from "~/components/layout/logo/homarr-logo";
import { LoginForm } from "./_login-form";
export default async function Login() {
interface LoginProps {
searchParams: {
callbackUrl?: string;
};
}
export default async function Login({ searchParams }: LoginProps) {
const session = await auth();
if (session) {
redirect(searchParams.callbackUrl ?? "/");
}
const t = await getScopedI18n("user.page.login");
return (
@@ -21,7 +36,12 @@ export default async function Login() {
</Text>
</Stack>
<Card bg="dark.8" w={64 * 6} maw="90vw">
<LoginForm />
<LoginForm
providers={env.AUTH_PROVIDERS}
oidcClientName={env.AUTH_OIDC_CLIENT_NAME}
isOidcAutoLoginEnabled={env.AUTH_OIDC_AUTO_LOGIN}
callbackUrl={searchParams.callbackUrl ?? "/"}
/>
</Card>
</Stack>
</Center>
@@ -44,7 +44,9 @@ export const ClientBoard = () => {
const board = useRequiredBoard();
const isReady = useIsBoardReady();
const sortedSections = board.sections.sort((sectionA, sectionB) => sectionA.position - sectionB.position);
const fullWidthSortedSections = board.sections
.filter((section) => section.kind === "empty" || section.kind === "category")
.sort((sectionA, sectionB) => sectionA.yOffset - sectionB.yOffset);
const ref = useRef<HTMLDivElement>(null);
@@ -58,11 +60,11 @@ export const ClientBoard = () => {
h={fullHeightWithoutHeaderAndFooter}
/>
<Stack ref={ref} h="100%" style={{ visibility: isReady ? "visible" : "hidden" }}>
{sortedSections.map((section) =>
{fullWidthSortedSections.map((section) =>
section.kind === "empty" ? (
<BoardEmptySection key={section.id} section={section} mainRef={ref} />
<BoardEmptySection key={section.id} section={section} />
) : (
<BoardCategorySection key={section.id} section={section} mainRef={ref} />
<BoardCategorySection key={section.id} section={section} />
),
)}
</Stack>
@@ -49,6 +49,7 @@ export const BoardProvider = ({
useEffect(() => {
setReadySections((previous) => previous.filter((id) => data.sections.some((section) => section.id === id)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data.sections.length, setReadySections]);
const markAsReady = useCallback((id: string) => {
@@ -4,6 +4,9 @@ import { TRPCError } from "@trpc/server";
// Placed here because gridstack styles are used for board content
import "~/styles/gridstack.scss";
import { IntegrationProvider } from "@homarr/auth/client";
import { auth } from "@homarr/auth/next";
import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server";
import { getI18n } from "@homarr/translation/server";
import { createMetaTitle } from "~/metadata";
@@ -25,10 +28,17 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
layout: createBoardLayout({
headerActions: <BoardContentHeaderActions />,
getInitialBoardAsync: getInitialBoard,
isBoardContentPage: true,
}),
page: () => {
return <ClientBoard />;
// eslint-disable-next-line no-restricted-syntax
page: async () => {
const session = await auth();
const integrations = await getIntegrationsWithPermissionsAsync(session);
return (
<IntegrationProvider integrations={integrations}>
<ClientBoard />
</IntegrationProvider>
);
},
generateMetadataAsync: async ({ params }: { params: TParams }): Promise<Metadata> => {
try {
@@ -39,6 +49,10 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
title: board.metaTitle ?? createMetaTitle(t("board.content.metaTitle", { boardName: board.name })),
icons: {
icon: board.faviconImageUrl ? board.faviconImageUrl : undefined,
apple: board.faviconImageUrl ? board.faviconImageUrl : undefined,
},
appleWebApp: {
startupImage: { url: board.faviconImageUrl ? board.faviconImageUrl : "/logo/logo.png" },
},
};
} catch (error) {
@@ -2,27 +2,29 @@
import { useCallback } from "react";
import { Group, Menu } from "@mantine/core";
import { useHotkeys } from "@mantine/hooks";
import {
IconBox,
IconBoxAlignTop,
IconChevronDown,
IconPackageImport,
IconPencil,
IconPencilOff,
IconPlus,
IconResize,
IconSettings,
} from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useModalAction } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { ItemSelectModal } from "~/components/board/items/item-select-modal";
import { useBoardPermissions } from "~/components/board/permissions/client";
import { useCategoryActions } from "~/components/board/sections/category/category-actions";
import { CategoryEditModal } from "~/components/board/sections/category/category-edit-modal";
import { useDynamicSectionActions } from "~/components/board/sections/dynamic/dynamic-actions";
import { HeaderButton } from "~/components/layout/header/button";
import { useEditMode, useRequiredBoard } from "./_context";
@@ -52,6 +54,7 @@ const AddMenu = () => {
const { openModal: openCategoryEditModal } = useModalAction(CategoryEditModal);
const { openModal: openItemSelectModal } = useModalAction(ItemSelectModal);
const { addCategoryToEnd } = useCategoryActions();
const { addDynamicSection } = useDynamicSectionActions();
const t = useI18n();
const handleAddCategory = useCallback(
@@ -92,13 +95,16 @@ const AddMenu = () => {
<Menu.Item leftSection={<IconBox size={20} />} onClick={handleSelectItem}>
{t("item.action.create")}
</Menu.Item>
<Menu.Item leftSection={<IconPackageImport size={20} />}>{t("item.action.import")}</Menu.Item>
<Menu.Divider />
<Menu.Item leftSection={<IconBoxAlignTop size={20} />} onClick={handleAddCategory}>
{t("section.category.action.create")}
</Menu.Item>
<Menu.Item leftSection={<IconResize size={20} />} onClick={addDynamicSection}>
{t("section.dynamic.action.create")}
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
@@ -132,6 +138,8 @@ const EditModeMenu = () => {
setEditMode(true);
}, [board, isEditMode, saveBoard, setEditMode]);
useHotkeys([["mod+e", toggle]]);
return (
<HeaderButton onClick={toggle} loading={isPending}>
{isEditMode ? <IconPencilOff stroke={1.5} /> : <IconPencil stroke={1.5} />}
@@ -8,5 +8,4 @@ export default createBoardLayout<{ locale: string; name: string }>({
async getInitialBoardAsync({ name }) {
return await api.board.getBoardByName({ name });
},
isBoardContentPage: false,
});
@@ -1,101 +0,0 @@
"use client";
import { useState } from "react";
import { Group, Stack, Tabs } from "@mantine/core";
import { IconUser, IconUserDown, IconUsersGroup } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useScopedI18n } from "@homarr/translation/client";
import type { TablerIcon } from "@homarr/ui";
import { CountBadge } from "@homarr/ui";
import type { Board } from "../../_types";
import { GroupsForm } from "./_access/group-access";
import { InheritTable } from "./_access/inherit-access";
import { UsersForm } from "./_access/user-access";
interface Props {
board: Board;
initialPermissions: RouterOutputs["board"]["getBoardPermissions"];
}
export const AccessSettingsContent = ({ board, initialPermissions }: Props) => {
const { data: permissions } = clientApi.board.getBoardPermissions.useQuery(
{
id: board.id,
},
{
initialData: initialPermissions,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
);
const [counts, setCounts] = useState({
user: initialPermissions.userPermissions.length + (board.creator ? 1 : 0),
group: initialPermissions.groupPermissions.length,
});
return (
<Stack>
<Tabs color="red" defaultValue="user">
<Tabs.List grow>
<TabItem value="user" count={counts.user} icon={IconUser} />
<TabItem value="group" count={counts.group} icon={IconUsersGroup} />
<TabItem value="inherited" count={initialPermissions.inherited.length} icon={IconUserDown} />
</Tabs.List>
<Tabs.Panel value="user">
<UsersForm
board={board}
initialPermissions={permissions}
onCountChange={(callback) =>
setCounts(({ user, ...others }) => ({
user: callback(user),
...others,
}))
}
/>
</Tabs.Panel>
<Tabs.Panel value="group">
<GroupsForm
board={board}
initialPermissions={permissions}
onCountChange={(callback) =>
setCounts(({ group, ...others }) => ({
group: callback(group),
...others,
}))
}
/>
</Tabs.Panel>
<Tabs.Panel value="inherited">
<InheritTable initialPermissions={permissions} />
</Tabs.Panel>
</Tabs>
</Stack>
);
};
interface TabItemProps {
value: "user" | "group" | "inherited";
count: number;
icon: TablerIcon;
}
const TabItem = ({ value, icon: Icon, count }: TabItemProps) => {
const t = useScopedI18n("board.setting.section.access.permission");
return (
<Tabs.Tab value={value} leftSection={<Icon stroke={1.5} size={16} />}>
<Group gap="sm">
{t(`tab.${value}`)}
<CountBadge count={count} />
</Group>
</Tabs.Tab>
);
};
@@ -1,13 +0,0 @@
import type { BoardPermission } from "@homarr/definitions";
import { createFormContext } from "@homarr/form";
export interface BoardAccessFormType {
items: {
itemId: string;
permission: BoardPermission;
}[];
}
export const [FormProvider, useFormContext, useForm] = createFormContext<BoardAccessFormType>();
export type OnCountChange = (callback: (prev: number) => number) => void;
@@ -1,57 +0,0 @@
import { Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { getPermissionsWithChildren } from "@homarr/definitions";
import type { BoardPermission, GroupPermissionKey } from "@homarr/definitions";
import { useScopedI18n } from "@homarr/translation/client";
import { BoardAccessDisplayRow } from "./board-access-table-rows";
import { GroupItemContent } from "./group-access";
export interface InheritTableProps {
initialPermissions: RouterOutputs["board"]["getBoardPermissions"];
}
const mapPermissions = {
"board-full-access": "board-full",
"board-modify-all": "board-change",
"board-view-all": "board-view",
} satisfies Partial<Record<GroupPermissionKey, BoardPermission | "board-full">>;
export const InheritTable = ({ initialPermissions }: InheritTableProps) => {
const tPermissions = useScopedI18n("board.setting.section.access.permission");
return (
<Stack pt="sm">
<Table>
<TableThead>
<TableTr>
<TableTh>{tPermissions("field.user.label")}</TableTh>
<TableTh>{tPermissions("field.permission.label")}</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{initialPermissions.inherited.map(({ group, permission }) => {
const boardPermission =
permission in mapPermissions
? mapPermissions[permission as keyof typeof mapPermissions]
: getPermissionsWithChildren([permission]).includes("board-full-access")
? "board-full"
: null;
if (!boardPermission) {
return null;
}
return (
<BoardAccessDisplayRow
key={group.id}
itemContent={<GroupItemContent group={group} />}
permission={boardPermission}
/>
);
})}
</TableTbody>
</Table>
</Stack>
);
};
@@ -1,137 +0,0 @@
import { useCallback, useState } from "react";
import Link from "next/link";
import { Anchor, Box, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
import { IconPlus } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useModalAction } from "@homarr/modals";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { UserAvatar } from "@homarr/ui";
import type { Board } from "../../../_types";
import { BoardAccessDisplayRow, BoardAccessSelectRow } from "./board-access-table-rows";
import type { BoardAccessFormType, OnCountChange } from "./form";
import { FormProvider, useForm } from "./form";
import { UserSelectModal } from "./user-select-modal";
export interface FormProps {
board: Pick<Board, "id" | "creatorId" | "creator">;
initialPermissions: RouterOutputs["board"]["getBoardPermissions"];
onCountChange: OnCountChange;
}
export const UsersForm = ({ board, initialPermissions, onCountChange }: FormProps) => {
const { mutate, isPending } = clientApi.board.saveUserBoardPermissions.useMutation();
const utils = clientApi.useUtils();
const [users, setUsers] = useState<Map<string, User>>(
new Map(initialPermissions.userPermissions.map(({ user }) => [user.id, user])),
);
const { openModal } = useModalAction(UserSelectModal);
const t = useI18n();
const tPermissions = useScopedI18n("board.setting.section.access.permission");
const form = useForm({
initialValues: {
items: initialPermissions.userPermissions.map(({ user, permission }) => ({
itemId: user.id,
permission,
})),
},
});
const handleSubmit = useCallback(
(values: BoardAccessFormType) => {
mutate(
{
id: board.id,
permissions: values.items,
},
{
onSuccess: () => {
void utils.board.getBoardPermissions.invalidate();
},
},
);
},
[board.id, mutate, utils.board.getBoardPermissions],
);
const handleAddUser = useCallback(() => {
const presentUserIds = form.values.items.map(({ itemId: id }) => id);
openModal({
presentUserIds: board.creatorId ? presentUserIds.concat(board.creatorId) : presentUserIds,
onSelect: (user) => {
setUsers((prev) => new Map(prev).set(user.id, user));
form.setFieldValue("items", [
{
itemId: user.id,
permission: "board-view",
},
...form.values.items,
]);
onCountChange((prev) => prev + 1);
},
});
}, [form, openModal, board.creatorId, onCountChange]);
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<FormProvider form={form}>
<Stack pt="sm">
<Table>
<TableThead>
<TableTr>
<TableTh>{tPermissions("field.user.label")}</TableTh>
<TableTh>{tPermissions("field.permission.label")}</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{board.creator && (
<BoardAccessDisplayRow itemContent={<UserItemContent user={board.creator} />} permission="board-full" />
)}
{form.values.items.map((row, index) => (
<BoardAccessSelectRow
key={row.itemId}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
itemContent={<UserItemContent user={users.get(row.itemId)!} />}
permission={row.permission}
index={index}
onCountChange={onCountChange}
/>
))}
</TableTbody>
</Table>
<Group justify="space-between">
<Button rightSection={<IconPlus size="1rem" />} variant="light" onClick={handleAddUser}>
{t("common.action.add")}
</Button>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
</Stack>
</FormProvider>
</form>
);
};
const UserItemContent = ({ user }: { user: User }) => {
return (
<Group wrap="nowrap">
<Box visibleFrom="xs">
<UserAvatar user={user} size="sm" />
</Box>
<Anchor component={Link} href={`/manage/users/${user.id}`} size="sm" style={{ whiteSpace: "nowrap" }}>
{user.name}
</Anchor>
</Group>
);
};
interface User {
id: string;
name: string | null;
image: string | null;
}
@@ -0,0 +1,69 @@
"use client";
import { IconEye, IconPencil, IconSettings } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { boardPermissions, boardPermissionsMap } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import { AccessSettings } from "~/components/access/access-settings";
import type { Board } from "../../_types";
interface Props {
board: Board;
initialPermissions: RouterOutputs["board"]["getBoardPermissions"];
}
export const BoardAccessSettings = ({ board, initialPermissions }: Props) => {
const groupMutation = clientApi.board.saveGroupBoardPermissions.useMutation();
const userMutation = clientApi.board.saveUserBoardPermissions.useMutation();
const utils = clientApi.useUtils();
const t = useI18n();
const { data: permissions } = clientApi.board.getBoardPermissions.useQuery(
{
id: board.id,
},
{
initialData: initialPermissions,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
);
return (
<AccessSettings
entity={{
id: board.id,
ownerId: board.creatorId,
owner: board.creator,
}}
query={{
invalidate: () => utils.board.getBoardPermissions.invalidate(),
data: permissions,
}}
groupsMutation={{
mutate: groupMutation.mutate,
isPending: groupMutation.isPending,
}}
usersMutation={{
mutate: userMutation.mutate,
isPending: userMutation.isPending,
}}
translate={(key) => t(`board.setting.section.access.permission.item.${key}.label`)}
permission={{
items: boardPermissions,
default: "view",
fullAccessGroupPermission: "board-full-all",
groupPermissionMapping: boardPermissionsMap,
icons: {
modify: IconPencil,
view: IconEye,
full: IconSettings,
},
}}
/>
);
};
@@ -20,8 +20,8 @@ import type { TablerIcon } from "@homarr/ui";
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
import { ActiveTabAccordion } from "../../../../../components/active-tab-accordion";
import { AccessSettingsContent } from "./_access";
import { BackgroundSettingsContent } from "./_background";
import { BoardAccessSettings } from "./_board-access";
import { ColorSettingsContent } from "./_colors";
import { CustomCssSettingsContent } from "./_customCss";
import { DangerZoneSettingsContent } from "./_danger";
@@ -44,8 +44,8 @@ const getBoardAndPermissionsAsync = async (params: Props["params"]) => {
const permissions = hasFullAccess
? await api.board.getBoardPermissions({ id: board.id })
: {
userPermissions: [],
groupPermissions: [],
users: [],
groups: [],
inherited: [],
};
@@ -89,7 +89,7 @@ export default async function BoardSettingsPage({ params, searchParams }: Props)
{hasFullAccess && (
<>
<AccordionItemFor value="access" icon={IconUser}>
<AccessSettingsContent board={board} initialPermissions={permissions} />
<BoardAccessSettings board={board} initialPermissions={permissions} />
</AccordionItemFor>
<AccordionItemFor value="dangerZone" icon={IconAlertTriangle} danger noPadding>
<DangerZoneSettingsContent />
@@ -4,7 +4,6 @@ import { AppShellMain } from "@mantine/core";
import { TRPCError } from "@trpc/server";
import { logger } from "@homarr/log";
import { GlobalItemServerDataRunner } from "@homarr/widgets";
import { MainHeader } from "~/components/layout/header";
import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo";
@@ -18,13 +17,11 @@ import { BoardMantineProvider } from "./(content)/_theme";
interface CreateBoardLayoutProps<TParams extends Params> {
headerActions: JSX.Element;
getInitialBoardAsync: (params: TParams) => Promise<Board>;
isBoardContentPage: boolean;
}
export const createBoardLayout = <TParams extends Params>({
headerActions,
getInitialBoardAsync: getInitialBoard,
isBoardContentPage,
}: CreateBoardLayoutProps<TParams>) => {
const Layout = async ({
params,
@@ -42,21 +39,19 @@ export const createBoardLayout = <TParams extends Params>({
});
return (
<GlobalItemServerDataRunner board={initialBoard} shouldRun={isBoardContentPage}>
<BoardProvider initialBoard={initialBoard}>
<BoardMantineProvider>
<CustomCss />
<ClientShell hasNavigation={false}>
<MainHeader
logo={<BoardLogoWithTitle size="md" hideTitleOnMobile />}
actions={headerActions}
hasNavigation={false}
/>
<AppShellMain>{children}</AppShellMain>
</ClientShell>
</BoardMantineProvider>
</BoardProvider>
</GlobalItemServerDataRunner>
<BoardProvider initialBoard={initialBoard}>
<BoardMantineProvider>
<CustomCss />
<ClientShell hasNavigation={false}>
<MainHeader
logo={<BoardLogoWithTitle size="md" hideTitleOnMobile />}
actions={headerActions}
hasNavigation={false}
/>
<AppShellMain>{children}</AppShellMain>
</ClientShell>
</BoardMantineProvider>
</BoardProvider>
);
};
@@ -7,5 +7,6 @@ export type Item = Section["items"][number];
export type CategorySection = Extract<Section, { kind: "category" }>;
export type EmptySection = Extract<Section, { kind: "empty" }>;
export type DynamicSection = Extract<Section, { kind: "dynamic" }>;
export type ItemOfKind<TKind extends WidgetKind> = Extract<Item, { kind: TKind }>;
@@ -7,6 +7,7 @@ import { clientApi } from "@homarr/api/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { CustomPasswordInput } from "@homarr/ui";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
@@ -50,7 +51,11 @@ export const InitUserForm = () => {
>
<Stack gap="lg">
<TextInput label={t("field.username.label")} {...form.getInputProps("username")} />
<PasswordInput label={t("field.password.label")} {...form.getInputProps("password")} />
<CustomPasswordInput
withPasswordRequirements
label={t("field.password.label")}
{...form.getInputProps("password")}
/>
<PasswordInput label={t("field.passwordConfirm.label")} {...form.getInputProps("confirmPassword")} />
<Button type="submit" fullWidth loading={isPending}>
{t("action.create")}
+41 -26
View File
@@ -1,18 +1,23 @@
import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google";
import "@homarr/ui/styles.css";
import "@homarr/notifications/styles.css";
import "@homarr/spotlight/styles.css";
import "@homarr/ui/styles.css";
import "~/styles/scroll-area.scss";
import { ColorSchemeScript, createTheme, MantineProvider } from "@mantine/core";
import { cookies } from "next/headers";
import { env } from "@homarr/auth/env.mjs";
import { auth } from "@homarr/auth/next";
import { ModalProvider } from "@homarr/modals";
import { Notifications } from "@homarr/notifications";
import { getScopedI18n } from "@homarr/translation/server";
import { Analytics } from "~/components/layout/analytics";
import { SearchEngineOptimization } from "~/components/layout/search-engine-optimization";
import { JotaiProvider } from "./_client-providers/jotai";
import { CustomMantineProvider } from "./_client-providers/mantine";
import { NextInternationalProvider } from "./_client-providers/next-international";
import { AuthProvider } from "./_client-providers/session";
import { TRPCReactProvider } from "./_client-providers/trpc";
@@ -23,8 +28,7 @@ const fontSans = Inter({
variable: "--font-sans",
});
export const metadata: Metadata = {
metadataBase: new URL("http://localhost:3000"),
export const generateMetadata = (): Metadata => ({
title: "Homarr",
description:
"Simplify the management of your server with Homarr - a sleek, modern dashboard that puts all of your apps and services at your fingertips.",
@@ -35,12 +39,17 @@ export const metadata: Metadata = {
url: "https://homarr.dev",
siteName: "Homarr Documentation",
},
twitter: {
card: "summary_large_image",
site: "@jullerino",
creator: "@jullerino",
icons: {
icon: "/logo/logo.png",
apple: "/logo/logo.png",
},
};
appleWebApp: {
title: "Homarr",
capable: true,
startupImage: { url: "/logo/logo.png" },
statusBarStyle: getColorScheme() === "dark" ? "black-translucent" : "default",
},
});
export const viewport: Viewport = {
themeColor: [
@@ -49,35 +58,37 @@ export const viewport: Viewport = {
],
};
export default function Layout(props: { children: React.ReactNode; params: { locale: string } }) {
const colorScheme = "dark";
export default async function Layout(props: { children: React.ReactNode; params: { locale: string } }) {
const session = await auth();
const colorScheme = getColorScheme();
const tCommon = await getScopedI18n("common");
const direction = tCommon("direction");
const StackedProvider = composeWrappers([
async (innerProps) => {
const session = await auth();
return <AuthProvider session={session} {...innerProps} />;
(innerProps) => {
return <AuthProvider session={session} logoutUrl={env.AUTH_LOGOUT_REDIRECT_URL} {...innerProps} />;
},
(innerProps) => <JotaiProvider {...innerProps} />,
(innerProps) => <TRPCReactProvider {...innerProps} />,
(innerProps) => <NextInternationalProvider {...innerProps} locale={props.params.locale} />,
(innerProps) => (
<MantineProvider
{...innerProps}
defaultColorScheme="dark"
theme={createTheme({
primaryColor: "red",
autoContrast: true,
})}
/>
),
(innerProps) => <CustomMantineProvider {...innerProps} />,
(innerProps) => <ModalProvider {...innerProps} />,
]);
return (
<html lang="en" suppressHydrationWarning>
// Instead of ColorSchemScript we use data-mantine-color-scheme to prevent flickering
<html
lang="en"
dir={direction}
data-mantine-color-scheme={colorScheme}
style={{
backgroundColor: colorScheme === "dark" ? "#242424" : "#fff",
}}
suppressHydrationWarning
>
<head>
<ColorSchemeScript defaultColorScheme={colorScheme} />
<Analytics />
<SearchEngineOptimization />
</head>
<body className={["font-sans", fontSans.variable].join(" ")}>
<StackedProvider>
@@ -88,3 +99,7 @@ export default function Layout(props: { children: React.ReactNode; params: { loc
</html>
);
}
const getColorScheme = () => {
return cookies().get("homarr-color-scheme")?.value ?? "dark";
};
@@ -19,7 +19,7 @@
transform: translateY(0);
}
100% {
transform: translateY(-50%);
transform: translateY(-50.8%);
}
}
@@ -1,3 +1,4 @@
import { headers } from "next/headers";
import Image from "next/image";
import {
Accordion,
@@ -17,16 +18,15 @@ import {
Title,
} from "@mantine/core";
import { IconLanguage, IconLibrary, IconUsers } from "@tabler/icons-react";
import { setStaticParamsLocale } from "next-international/server";
import { getScopedI18n, getStaticParams } from "@homarr/translation/server";
import { getScopedI18n } from "@homarr/translation/server";
import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { createMetaTitle } from "~/metadata";
import { getPackageAttributesAsync } from "~/versions/package-reader";
import contributorsData from "../../../../../../../static-data/contributors.json";
import translatorsData from "../../../../../../../static-data/translators.json";
import type githubContributorsJson from "../../../../../../../static-data/contributors.json";
import type crowdinContributorsJson from "../../../../../../../static-data/translators.json";
import classes from "./about.module.css";
export async function generateMetadata() {
@@ -37,16 +37,26 @@ export async function generateMetadata() {
};
}
interface PageProps {
params: {
locale: string;
};
}
const getHost = () => {
if (process.env.HOSTNAME) {
return `${process.env.HOSTNAME}:3000`;
}
export default async function AboutPage({ params: { locale } }: PageProps) {
setStaticParamsLocale(locale);
return headers().get("host");
};
export default async function AboutPage() {
const baseServerUrl = `http://${getHost()}`;
const t = await getScopedI18n("management.page.about");
const attributes = await getPackageAttributesAsync();
const githubContributors = (await fetch(`${baseServerUrl}/api/about/contributors/github`).then((res) =>
res.json(),
)) as typeof githubContributorsJson;
const crowdinContributors = (await fetch(`${baseServerUrl}/api/about/contributors/crowdin`).then((res) =>
res.json(),
)) as typeof crowdinContributorsJson;
return (
<div>
<DynamicBreadcrumb />
@@ -70,14 +80,14 @@ export default async function AboutPage({ params: { locale } }: PageProps) {
<Text>{t("accordion.contributors.title")}</Text>
<Text size="sm" c="dimmed">
{t("accordion.contributors.subtitle", {
count: contributorsData.length,
count: githubContributors.length,
})}
</Text>
</Stack>
</AccordionControl>
<AccordionPanel>
<Flex wrap="wrap" gap="xs">
{contributorsData.map((contributor) => (
{githubContributors.map((contributor) => (
<GenericContributorLinkCard
key={contributor.login}
link={`https://github.com/${contributor.login}`}
@@ -94,14 +104,14 @@ export default async function AboutPage({ params: { locale } }: PageProps) {
<Text>{t("accordion.translators.title")}</Text>
<Text size="sm" c="dimmed">
{t("accordion.translators.subtitle", {
count: translatorsData.length,
count: crowdinContributors.length,
})}
</Text>
</Stack>
</AccordionControl>
<AccordionPanel>
<Flex wrap="wrap" gap="xs">
{translatorsData.map((translator) => (
{crowdinContributors.map((translator) => (
<GenericContributorLinkCard
key={translator.username}
link={`https://crowdin.com/profile/${translator.username}`}
@@ -164,9 +174,3 @@ const GenericContributorLinkCard = ({ name, image, link }: GenericContributorLin
</AspectRatio>
);
};
export function generateStaticParams() {
return getStaticParams();
}
export const dynamic = "force-static";
@@ -6,12 +6,11 @@ import { IconTrash } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "../../../revalidatePathAction";
interface AppDeleteButtonProps {
app: RouterOutputs["app"]["all"][number];
}
@@ -5,12 +5,12 @@ import { useRouter } from "next/navigation";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { TranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import type { validation, z } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { AppForm } from "../../_form";
interface AppEditFormProps {
@@ -4,12 +4,12 @@ import { useCallback } from "react";
import { useRouter } from "next/navigation";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { TranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import type { validation, z } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { AppForm } from "../_form";
export const AppNewForm = () => {
@@ -4,6 +4,7 @@ import { IconApps, IconPencil } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
import { parseAppHrefWithVariablesServer } from "@homarr/common/server";
import { getI18n, getScopedI18n } from "@homarr/translation/server";
import { ManageContainer } from "~/components/manage/manage-container";
@@ -69,8 +70,8 @@ const AppCard = async ({ app }: AppCardProps) => {
</Text>
)}
{app.href && (
<Anchor href={app.href} lineClamp={1} size="sm" w="min-content">
{app.href}
<Anchor href={parseAppHrefWithVariablesServer(app.href)} lineClamp={1} size="sm" w="min-content">
{parseAppHrefWithVariablesServer(app.href)}
</Anchor>
)}
</Stack>
@@ -104,7 +105,7 @@ const AppNoResults = async () => {
<Text fw={500} size="lg">
{t("app.page.list.noResults.title")}
</Text>
<Anchor href="/manage/apps/new">{t("app.page.list.noResults.description")}</Anchor>
<Anchor href="/manage/apps/new">{t("app.page.list.noResults.action")}</Anchor>
</Stack>
</Card>
);
@@ -7,10 +7,10 @@ import { IconHome, IconSettings, IconTrash } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { useBoardPermissions } from "~/components/board/permissions/client";
const iconProps = {
@@ -1,44 +1,47 @@
"use client";
import { useCallback } from "react";
import { IconCategoryPlus } from "@tabler/icons-react";
import { Affix, Button, Group, Menu } from "@mantine/core";
import { IconCategoryPlus, IconChevronDown, IconFileImport } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useModalAction } from "@homarr/modals";
import { AddBoardModal, ImportBoardModal } from "@homarr/modals-collection";
import { useI18n } from "@homarr/translation/client";
import { BetaBadge } from "@homarr/ui";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { AddBoardModal } from "~/components/manage/boards/add-board-modal";
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
interface CreateBoardButtonProps {
boardNames: string[];
}
export const CreateBoardButton = ({ boardNames }: CreateBoardButtonProps) => {
export const CreateBoardButton = () => {
const t = useI18n();
const { openModal } = useModalAction(AddBoardModal);
const { openModal: openAddModal } = useModalAction(AddBoardModal);
const { openModal: openImportModal } = useModalAction(ImportBoardModal);
const { mutateAsync, isPending } = clientApi.board.createBoard.useMutation({
onSettled: async () => {
await revalidatePathActionAsync("/manage/boards");
},
});
const onClick = useCallback(() => {
openModal({
onSuccess: async (values) => {
await mutateAsync({
name: values.name,
});
},
boardNames,
});
}, [mutateAsync, boardNames, openModal]);
const buttonGroupContent = (
<>
<Button leftSection={<IconCategoryPlus size="1rem" />} onClick={openAddModal}>
{t("management.page.board.action.new.label")}
</Button>
<Menu position="bottom-end">
<Menu.Target>
<Button px="xs" ms={1}>
<IconChevronDown size="1rem" />
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={openImportModal} leftSection={<IconFileImport size="1rem" />}>
<Group>
{t("board.action.oldImport.label")}
<BetaBadge size="xs" />
</Group>
</Menu.Item>
</Menu.Dropdown>
</Menu>
</>
);
return (
<MobileAffixButton leftSection={<IconCategoryPlus size="1rem" />} onClick={onClick} loading={isPending}>
{t("management.page.board.action.new.label")}
</MobileAffixButton>
<>
<Button.Group visibleFrom="md">{buttonGroupContent}</Button.Group>
<Affix hiddenFrom="md" position={{ bottom: 20, right: 20 }}>
<Button.Group>{buttonGroupContent}</Button.Group>
</Affix>
</>
);
};
@@ -19,6 +19,7 @@ import { IconDotsVertical, IconHomeFilled, IconLock, IconWorld } from "@tabler/i
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { getScopedI18n } from "@homarr/translation/server";
import { UserAvatar } from "@homarr/ui";
@@ -30,8 +31,9 @@ import { CreateBoardButton } from "./_components/create-board-button";
export default async function ManageBoardsPage() {
const t = await getScopedI18n("management.page.board");
const session = await auth();
const boards = await api.board.getAllBoards();
const canCreateBoards = session?.user.permissions.includes("board-create");
return (
<ManageContainer>
@@ -39,7 +41,7 @@ export default async function ManageBoardsPage() {
<Stack>
<Group justify="space-between">
<Title mb="md">{t("title")}</Title>
<CreateBoardButton boardNames={boards.map((board) => board.name)} />
{canCreateBoards && <CreateBoardButton />}
</Group>
<Grid mb={{ base: "xl", md: 0 }}>
@@ -0,0 +1,61 @@
"use client";
import { IconPlayerPlay, IconSelector, IconSettings } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { integrationPermissions, integrationPermissionsMap } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import { AccessSettings } from "~/components/access/access-settings";
interface Props {
integration: RouterOutputs["integration"]["byId"];
initialPermissions: RouterOutputs["integration"]["getIntegrationPermissions"];
}
export const IntegrationAccessSettings = ({ integration, initialPermissions }: Props) => {
const t = useI18n();
const utils = clientApi.useUtils();
const { data } = clientApi.integration.getIntegrationPermissions.useQuery(
{
id: integration.id,
},
{
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
initialData: initialPermissions,
},
);
const usersMutation = clientApi.integration.saveUserIntegrationPermissions.useMutation();
const groupsMutation = clientApi.integration.saveGroupIntegrationPermissions.useMutation();
return (
<AccessSettings
entity={{
id: integration.id,
ownerId: null,
owner: null,
}}
permission={{
items: integrationPermissions,
default: "use",
fullAccessGroupPermission: "integration-full-all",
icons: {
use: IconSelector,
interact: IconPlayerPlay,
full: IconSettings,
},
groupPermissionMapping: integrationPermissionsMap,
}}
translate={(key) => t(`integration.permission.${key}`)}
query={{
data,
invalidate: () => utils.integration.getIntegrationPermissions.invalidate(),
}}
groupsMutation={groupsMutation}
usersMutation={usersMutation}
/>
);
};
@@ -5,12 +5,11 @@ import { ActionIcon } from "@mantine/core";
import { IconTrash } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "../../../revalidatePathAction";
interface DeleteIntegrationActionButtonProps {
count: number;
integration: { id: string; name: string };
@@ -6,6 +6,7 @@ import { Button, Fieldset, Group, Stack, TextInput } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { getAllSecretKindOptions, getDefaultSecretKinds } from "@homarr/definitions";
import { useZodForm } from "@homarr/form";
import { convertIntegrationTestConnectionError } from "@homarr/integrations/client";
@@ -15,7 +16,6 @@ import { useI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { SecretCard } from "../../_components/secrets/integration-secret-card";
import { IntegrationSecretInput } from "../../_components/secrets/integration-secret-inputs";
@@ -1,11 +1,13 @@
import { Container, Group, Stack, Title } from "@mantine/core";
import { Container, Fieldset, Group, Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { getIntegrationName } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import { getI18n, getScopedI18n } from "@homarr/translation/server";
import { IntegrationAvatar } from "@homarr/ui";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { IntegrationAvatar } from "../../_integration-avatar";
import { catchTrpcNotFound } from "~/errors/trpc-not-found";
import { IntegrationAccessSettings } from "../../_components/integration-access-settings";
import { EditIntegrationForm } from "./_integration-edit-form";
interface EditIntegrationPageProps {
@@ -13,8 +15,10 @@ interface EditIntegrationPageProps {
}
export default async function EditIntegrationPage({ params }: EditIntegrationPageProps) {
const t = await getScopedI18n("integration.page.edit");
const integration = await api.integration.byId({ id: params.id });
const editT = await getScopedI18n("integration.page.edit");
const t = await getI18n();
const integration = await api.integration.byId({ id: params.id }).catch(catchTrpcNotFound);
const integrationPermissions = await api.integration.getIntegrationPermissions({ id: integration.id });
return (
<>
@@ -23,9 +27,14 @@ export default async function EditIntegrationPage({ params }: EditIntegrationPag
<Stack>
<Group align="center">
<IntegrationAvatar kind={integration.kind} size="md" />
<Title>{t("title", { name: getIntegrationName(integration.kind) })}</Title>
<Title>{editT("title", { name: getIntegrationName(integration.kind) })}</Title>
</Group>
<EditIntegrationForm integration={integration} />
<Title order={2}>{t("permission.title")}</Title>
<Fieldset>
<IntegrationAccessSettings integration={integration} initialPermissions={integrationPermissions} />
</Fieldset>
</Stack>
</Container>
</>
@@ -8,8 +8,7 @@ import { IconSearch } from "@tabler/icons-react";
import { getIntegrationName, integrationKinds } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import { IntegrationAvatar } from "../_integration-avatar";
import { IntegrationAvatar } from "@homarr/ui";
export const IntegrationCreateDropdownContent = () => {
const t = useI18n();
@@ -3,11 +3,13 @@
import { useCallback } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { Button, Fieldset, Group, SegmentedControl, Stack, TextInput } from "@mantine/core";
import { Alert, Button, Fieldset, Group, SegmentedControl, Stack, Text, TextInput } from "@mantine/core";
import { IconInfoCircle } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import type { IntegrationKind, IntegrationSecretKind } from "@homarr/definitions";
import { getAllSecretKindOptions } from "@homarr/definitions";
import { getAllSecretKindOptions, getIntegrationName } from "@homarr/definitions";
import type { UseFormReturnType } from "@homarr/form";
import { useZodForm } from "@homarr/form";
import { convertIntegrationTestConnectionError } from "@homarr/integrations/client";
@@ -17,7 +19,6 @@ import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
import { IntegrationSecretInput } from "../_components/secrets/integration-secret-inputs";
import { revalidatePathActionAsync } from "../../../../revalidatePathAction";
interface NewIntegrationFormProps {
searchParams: Partial<z.infer<typeof validation.integration.create>> & {
@@ -31,7 +32,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
const router = useRouter();
const form = useZodForm(validation.integration.create.omit({ kind: true }), {
initialValues: {
name: searchParams.name ?? "",
name: searchParams.name ?? getIntegrationName(searchParams.kind),
url: searchParams.url ?? "",
secrets: secretKinds[0].map((kind) => ({
kind,
@@ -80,7 +81,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
return (
<form onSubmit={form.onSubmit((value) => void handleSubmitAsync(value))}>
<Stack>
<TextInput withAsterisk label={t("integration.field.name.label")} {...form.getInputProps("name")} />
<TextInput withAsterisk label={t("integration.field.name.label")} autoFocus {...form.getInputProps("name")} />
<TextInput withAsterisk label={t("integration.field.url.label")} {...form.getInputProps("url")} />
@@ -95,6 +96,11 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
{...form.getInputProps(`secrets.${index}.value`)}
/>
))}
{form.values.secrets.length === 0 && (
<Alert icon={<IconInfoCircle size={"1rem"} />} color={"blue"}>
<Text c={"blue"}>{t("integration.secrets.noSecretsRequired.text")}</Text>
</Alert>
)}
</Stack>
</Fieldset>
@@ -120,12 +126,20 @@ const SecretKindsSegmentedControl = ({ secretKinds, form }: SecretKindsSegmented
const t = useScopedI18n("integration.secrets");
const secretKindGroups = secretKinds.map((kinds) => ({
label: kinds.map((kind) => t(`kind.${kind}.label`)).join(" & "),
value: kinds.join("-"),
label:
kinds.length === 0
? t("noSecretsRequired.segmentTitle")
: kinds.map((kind) => t(`kind.${kind}.label`)).join(" & "),
value: kinds.length === 0 ? "empty" : kinds.join("-"),
}));
const onChange = useCallback(
(value: string) => {
if (value === "empty") {
form.setFieldValue("secrets", []);
return;
}
const kinds = value.split("-") as IntegrationSecretKind[];
const secrets = kinds.map((kind) => ({
kind,
@@ -1,14 +1,15 @@
import { notFound } from "next/navigation";
import { Container, Group, Stack, Title } from "@mantine/core";
import { auth } from "@homarr/auth/next";
import type { IntegrationKind } from "@homarr/definitions";
import { getIntegrationName, integrationKinds } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import { IntegrationAvatar } from "@homarr/ui";
import type { validation } from "@homarr/validation";
import { z } from "@homarr/validation";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { IntegrationAvatar } from "../_integration-avatar";
import { NewIntegrationForm } from "./_integration-new-form";
interface NewIntegrationPageProps {
@@ -18,13 +19,17 @@ interface NewIntegrationPageProps {
}
export default async function IntegrationsNewPage({ searchParams }: NewIntegrationPageProps) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const result = z.enum([integrationKinds[0]!, ...integrationKinds.slice(1)]).safeParse(searchParams.kind);
const session = await auth();
if (!session?.user.permissions.includes("integration-create")) {
notFound();
}
const result = z.enum(integrationKinds).safeParse(searchParams.kind);
if (!result.success) {
notFound();
}
const t = await getScopedI18n("integration.page.create");
const tCreate = await getScopedI18n("integration.page.create");
const currentKind = result.data;
@@ -35,7 +40,7 @@ export default async function IntegrationsNewPage({ searchParams }: NewIntegrati
<Stack>
<Group align="center">
<IntegrationAvatar kind={currentKind} size="md" />
<Title>{t("title", { name: getIntegrationName(currentKind) })}</Title>
<Title>{tCreate("title", { name: getIntegrationName(currentKind) })}</Title>
</Group>
<NewIntegrationForm searchParams={searchParams} />
</Stack>
@@ -30,16 +30,16 @@ import { IconChevronDown, IconChevronUp, IconPencil } from "@tabler/icons-react"
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { objectEntries } from "@homarr/common";
import type { IntegrationKind } from "@homarr/definitions";
import { getIntegrationName } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import { CountBadge } from "@homarr/ui";
import { CountBadge, IntegrationAvatar } from "@homarr/ui";
import { ManageContainer } from "~/components/manage/manage-container";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { ActiveTabAccordion } from "../../../../components/active-tab-accordion";
import { IntegrationAvatar } from "./_integration-avatar";
import { DeleteIntegrationActionButton } from "./_integration-buttons";
import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown";
@@ -51,8 +51,11 @@ interface IntegrationsPageProps {
export default async function IntegrationsPage({ searchParams }: IntegrationsPageProps) {
const integrations = await api.integration.all();
const session = await auth();
const t = await getScopedI18n("integration");
const canCreateIntegrations = session?.user.permissions.includes("integration-create") ?? false;
return (
<ManageContainer>
<DynamicBreadcrumb />
@@ -60,23 +63,27 @@ export default async function IntegrationsPage({ searchParams }: IntegrationsPag
<Group justify="space-between" align="center">
<Title>{t("page.list.title")}</Title>
<Box>
<IntegrationSelectMenu>
<Affix hiddenFrom="md" position={{ bottom: 20, right: 20 }}>
<MenuTarget>
<Button rightSection={<IconChevronUp size={16} stroke={1.5} />}>{t("action.create")}</Button>
</MenuTarget>
</Affix>
</IntegrationSelectMenu>
</Box>
{canCreateIntegrations && (
<>
<Box>
<IntegrationSelectMenu>
<Affix hiddenFrom="md" position={{ bottom: 20, right: 20 }}>
<MenuTarget>
<Button rightSection={<IconChevronUp size={16} stroke={1.5} />}>{t("action.create")}</Button>
</MenuTarget>
</Affix>
</IntegrationSelectMenu>
</Box>
<Box visibleFrom="md">
<IntegrationSelectMenu>
<MenuTarget>
<Button rightSection={<IconChevronDown size={16} stroke={1.5} />}>{t("action.create")}</Button>
</MenuTarget>
</IntegrationSelectMenu>
</Box>
<Box visibleFrom="md">
<IntegrationSelectMenu>
<MenuTarget>
<Button rightSection={<IconChevronDown size={16} stroke={1.5} />}>{t("action.create")}</Button>
</MenuTarget>
</IntegrationSelectMenu>
</Box>
</>
)}
</Group>
<IntegrationList integrations={integrations} activeTab={searchParams.tab} />
@@ -103,6 +110,8 @@ interface IntegrationListProps {
const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps) => {
const t = await getScopedI18n("integration");
const session = await auth();
const hasFullAccess = session?.user.permissions.includes("integration-full-all") ?? false;
if (integrations.length === 0) {
return <div>{t("page.list.empty")}</div>;
@@ -152,18 +161,20 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
</TableTd>
<TableTd>
<Group justify="end">
<ActionIconGroup>
<ActionIcon
component={Link}
href={`/manage/integrations/edit/${integration.id}`}
variant="subtle"
color="gray"
aria-label={t("page.edit.title", { name: getIntegrationName(integration.kind) })}
>
<IconPencil size={16} stroke={1.5} />
</ActionIcon>
<DeleteIntegrationActionButton integration={integration} count={integrations.length} />
</ActionIconGroup>
{(hasFullAccess || integration.permissions.hasFullAccess) && (
<ActionIconGroup>
<ActionIcon
component={Link}
href={`/manage/integrations/edit/${integration.id}`}
variant="subtle"
color="gray"
aria-label={t("page.edit.title", { name: getIntegrationName(integration.kind) })}
>
<IconPencil size={16} stroke={1.5} />
</ActionIcon>
<DeleteIntegrationActionButton integration={integration} count={integrations.length} />
</ActionIconGroup>
)}
</Group>
</TableTd>
</TableTr>
@@ -178,18 +189,21 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
<Stack gap={0}>
<Group justify="space-between" align="center" wrap="nowrap">
<Text>{integration.name}</Text>
<ActionIconGroup>
<ActionIcon
component={Link}
href={`/manage/integrations/edit/${integration.id}`}
variant="subtle"
color="gray"
aria-label={t("page.edit.title", { name: getIntegrationName(integration.kind) })}
>
<IconPencil size={16} stroke={1.5} />
</ActionIcon>
<DeleteIntegrationActionButton integration={integration} count={integrations.length} />
</ActionIconGroup>
{hasFullAccess ||
(integration.permissions.hasFullAccess && (
<ActionIconGroup>
<ActionIcon
component={Link}
href={`/manage/integrations/edit/${integration.id}`}
variant="subtle"
color="gray"
aria-label={t("page.edit.title", { name: getIntegrationName(integration.kind) })}
>
<IconPencil size={16} stroke={1.5} />
</ActionIcon>
<DeleteIntegrationActionButton integration={integration} count={integrations.length} />
</ActionIconGroup>
))}
</Group>
<Anchor href={integration.url} target="_blank" rel="noreferrer" size="sm">
{integration.url}
+21 -2
View File
@@ -15,6 +15,7 @@ import {
IconPlug,
IconQuestionMark,
IconReport,
IconSearch,
IconSettings,
IconTool,
IconUser,
@@ -22,6 +23,9 @@ import {
IconUsersGroup,
} from "@tabler/icons-react";
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { createDocumentationLink } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import { MainHeader } from "~/components/layout/header";
@@ -31,6 +35,7 @@ import { ClientShell } from "~/components/layout/shell";
export default async function ManageLayout({ children }: PropsWithChildren) {
const t = await getScopedI18n("management.navbar");
const session = await auth();
const navigationLinks: NavigationLink[] = [
{
label: t("items.home"),
@@ -52,9 +57,15 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
href: "/manage/integrations",
label: t("items.integrations"),
},
{
icon: IconSearch,
href: "/manage/search-engines",
label: t("items.searchEngies"),
},
{
icon: IconUser,
label: t("items.users.label"),
hidden: !session?.user.permissions.includes("admin"),
items: [
{
label: t("items.users.items.manage"),
@@ -65,6 +76,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
label: t("items.users.items.invites"),
icon: IconMailForward,
href: "/manage/users/invites",
hidden: !isProviderEnabled("credentials"),
},
{
label: t("items.users.items.groups"),
@@ -76,12 +88,18 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
{
label: t("items.tools.label"),
icon: IconTool,
hidden: !session?.user.permissions.includes("admin"),
items: [
{
label: t("items.tools.items.docker"),
icon: IconBrandDocker,
href: "/manage/tools/docker",
},
{
label: t("items.tools.items.api"),
icon: IconPlug,
href: "/manage/tools/api",
},
{
label: t("items.tools.items.logs"),
icon: IconLogs,
@@ -98,6 +116,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
label: t("items.settings"),
href: "/manage/settings",
icon: IconSettings,
hidden: !session?.user.permissions.includes("admin"),
},
{
label: t("items.help.label"),
@@ -106,7 +125,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
{
label: t("items.help.items.documentation"),
icon: IconBook2,
href: "https://homarr.dev/docs/getting-started/prerequisites",
href: createDocumentationLink("/docs/getting-started"),
external: true,
},
{
@@ -116,7 +135,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
external: true,
},
{
label: t("items.tools.items.docker"),
label: t("items.help.items.discord"),
icon: IconBrandDiscord,
href: "https://discord.com/invite/aCsmEV5RgA",
external: true,
+36 -26
View File
@@ -3,6 +3,8 @@ import { Card, Group, SimpleGrid, Space, Stack, Text } 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 { getScopedI18n } from "@homarr/translation/server";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
@@ -14,6 +16,7 @@ interface LinkProps {
subtitle: string;
count: number;
href: string;
hidden?: boolean;
}
export async function generateMetadata() {
@@ -26,6 +29,7 @@ export async function generateMetadata() {
export default async function ManagementPage() {
const statistics = await api.home.getStats();
const session = await auth();
const t = await getScopedI18n("management.page.home");
const links: LinkProps[] = [
@@ -33,37 +37,40 @@ export default async function ManagementPage() {
count: statistics.countBoards,
href: "/manage/boards",
subtitle: t("statisticLabel.boards"),
title: t("statistic.countBoards"),
title: t("statistic.board"),
},
{
count: statistics.countUsers,
href: "/manage/boards",
href: "/manage/users",
subtitle: t("statisticLabel.authentication"),
title: t("statistic.createUser"),
title: t("statistic.user"),
hidden: !session?.user.permissions.includes("admin"),
},
{
count: statistics.countInvites,
href: "/manage/boards",
href: "/manage/users/invites",
subtitle: t("statisticLabel.authentication"),
title: t("statistic.createInvite"),
title: t("statistic.invite"),
hidden: !isProviderEnabled("credentials") || !session?.user.permissions.includes("admin"),
},
{
count: statistics.countIntegrations,
href: "/manage/integrations",
subtitle: t("statisticLabel.resources"),
title: t("statistic.addIntegration"),
title: t("statistic.integration"),
},
{
count: statistics.countApps,
href: "/manage/apps",
subtitle: t("statisticLabel.resources"),
title: t("statistic.addApp"),
title: t("statistic.app"),
},
{
count: statistics.countGroups,
href: "/manage/users/groups",
subtitle: t("statisticLabel.authorization"),
title: t("statistic.manageRoles"),
title: t("statistic.group"),
hidden: !session?.user.permissions.includes("admin"),
},
];
return (
@@ -72,24 +79,27 @@ export default async function ManagementPage() {
<HeroBanner />
<Space h="md" />
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3 }}>
{links.map((link, index) => (
<Card component={Link} href={link.href} key={`link-${index}`} withBorder>
<Group justify="space-between" wrap="nowrap">
<Group wrap="nowrap">
<Text size="2.4rem" fw="bolder">
{link.count}
</Text>
<Stack gap={0}>
<Text c="red" size="xs">
{link.subtitle}
</Text>
<Text fw="bold">{link.title}</Text>
</Stack>
</Group>
<IconArrowRight />
</Group>
</Card>
))}
{links.map(
(link) =>
!link.hidden && (
<Card component={Link} href={link.href} key={link.href} withBorder>
<Group justify="space-between" wrap="nowrap">
<Group wrap="nowrap">
<Text size="2.4rem" fw="bolder">
{link.count}
</Text>
<Stack gap={0}>
<Text c="red" size="xs">
{link.subtitle}
</Text>
<Text fw="bold">{link.title}</Text>
</Stack>
</Group>
<IconArrowRight />
</Group>
</Card>
),
)}
</SimpleGrid>
</>
);
@@ -0,0 +1,73 @@
"use client";
import Link from "next/link";
import { Button, Grid, Group, Stack, Textarea, TextInput } from "@mantine/core";
import { useZodForm } from "@homarr/form";
import type { TranslationFunction } from "@homarr/translation";
import { useI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
import { IconPicker } from "~/components/icons/picker/icon-picker";
type FormType = z.infer<typeof validation.searchEngine.manage>;
interface SearchEngineFormProps {
submitButtonTranslation: (t: TranslationFunction) => string;
initialValues?: FormType;
handleSubmit: (values: FormType) => void;
isPending: boolean;
disableShort?: boolean;
}
export const SearchEngineForm = (props: SearchEngineFormProps) => {
const { submitButtonTranslation, handleSubmit, initialValues, isPending, disableShort } = props;
const t = useI18n();
const form = useZodForm(validation.searchEngine.manage, {
initialValues: initialValues ?? {
name: "",
short: "",
iconUrl: "",
urlTemplate: "",
description: "",
},
});
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<Grid>
<Grid.Col span={{ base: 12, md: 8, lg: 9, xl: 10 }}>
<TextInput {...form.getInputProps("name")} withAsterisk label={t("search.engine.field.name.label")} />
</Grid.Col>
<Grid.Col span={{ base: 12, md: 4, lg: 3, xl: 2 }}>
<TextInput
{...form.getInputProps("short")}
disabled={disableShort}
withAsterisk
label={t("search.engine.field.short.label")}
/>
</Grid.Col>
</Grid>
<IconPicker initialValue={initialValues?.iconUrl} {...form.getInputProps("iconUrl")} />
<TextInput
{...form.getInputProps("urlTemplate")}
withAsterisk
label={t("search.engine.field.urlTemplate.label")}
/>
<Textarea {...form.getInputProps("description")} label={t("search.engine.field.description.label")} />
<Group justify="end">
<Button variant="default" component={Link} href="/manage/search-engines">
{t("common.action.backToOverview")}
</Button>
<Button type="submit" loading={isPending}>
{submitButtonTranslation(t)}
</Button>
</Group>
</Stack>
</form>
);
};
@@ -0,0 +1,55 @@
"use client";
import { useCallback } from "react";
import { ActionIcon } from "@mantine/core";
import { IconTrash } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
interface SearchEngineDeleteButtonProps {
searchEngine: RouterOutputs["searchEngine"]["getPaginated"]["items"][number];
}
export const SearchEngineDeleteButton = ({ searchEngine }: SearchEngineDeleteButtonProps) => {
const t = useScopedI18n("search.engine.page.delete");
const { openConfirmModal } = useConfirmModal();
const { mutate, isPending } = clientApi.searchEngine.delete.useMutation();
const onClick = useCallback(() => {
openConfirmModal({
title: t("title"),
children: t("message", searchEngine),
onConfirm: () => {
mutate(
{ id: searchEngine.id },
{
onSuccess: () => {
showSuccessNotification({
title: t("notification.success.title"),
message: t("notification.success.message"),
});
void revalidatePathActionAsync("/manage/search-engines");
},
onError: () => {
showErrorNotification({
title: t("notification.error.title"),
message: t("notification.error.message"),
});
},
},
);
},
});
}, [searchEngine, mutate, t, openConfirmModal]);
return (
<ActionIcon loading={isPending} variant="subtle" color="red" onClick={onClick} aria-label={t("title")}>
<IconTrash color="red" size={16} stroke={1.5} />
</ActionIcon>
);
};
@@ -0,0 +1,63 @@
"use client";
import { useCallback } from "react";
import { useRouter } from "next/navigation";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { TranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import type { validation, z } from "@homarr/validation";
import { SearchEngineForm } from "../../_form";
interface SearchEngineEditFormProps {
searchEngine: RouterOutputs["searchEngine"]["byId"];
}
export const SearchEngineEditForm = ({ searchEngine }: SearchEngineEditFormProps) => {
const t = useScopedI18n("search.engine.page.edit.notification");
const router = useRouter();
const { mutate, isPending } = clientApi.searchEngine.update.useMutation({
onSuccess: () => {
showSuccessNotification({
title: t("success.title"),
message: t("success.message"),
});
void revalidatePathActionAsync("/manage/search-engines").then(() => {
router.push("/manage/search-engines");
});
},
onError: () => {
showErrorNotification({
title: t("error.title"),
message: t("error.message"),
});
},
});
const handleSubmit = useCallback(
(values: z.infer<typeof validation.searchEngine.manage>) => {
mutate({
id: searchEngine.id,
...values,
});
},
[mutate, searchEngine.id],
);
const submitButtonTranslation = useCallback((t: TranslationFunction) => t("common.action.save"), []);
return (
<SearchEngineForm
submitButtonTranslation={submitButtonTranslation}
initialValues={searchEngine}
handleSubmit={handleSubmit}
isPending={isPending}
disableShort
/>
);
};
@@ -0,0 +1,27 @@
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { getI18n } from "@homarr/translation/server";
import { ManageContainer } from "~/components/manage/manage-container";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { SearchEngineEditForm } from "./_search-engine-edit-form";
interface SearchEngineEditPageProps {
params: { id: string };
}
export default async function SearchEngineEditPage({ params }: SearchEngineEditPageProps) {
const searchEngine = await api.searchEngine.byId({ id: params.id });
const t = await getI18n();
return (
<ManageContainer>
<DynamicBreadcrumb dynamicMappings={new Map([[params.id, searchEngine.name]])} nonInteractable={["edit"]} />
<Stack>
<Title>{t("search.engine.page.edit.title")}</Title>
<SearchEngineEditForm searchEngine={searchEngine} />
</Stack>
</ManageContainer>
);
}
@@ -0,0 +1,52 @@
"use client";
import { useCallback } from "react";
import { useRouter } from "next/navigation";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { TranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
import type { validation, z } from "@homarr/validation";
import { SearchEngineForm } from "../_form";
export const SearchEngineNewForm = () => {
const t = useScopedI18n("search.engine.page.create.notification");
const router = useRouter();
const { mutate, isPending } = clientApi.searchEngine.create.useMutation({
onSuccess: async () => {
showSuccessNotification({
title: t("success.title"),
message: t("success.message"),
});
await revalidatePathActionAsync("/manage/search-engines");
router.push("/manage/search-engines");
},
onError: () => {
showErrorNotification({
title: t("error.title"),
message: t("error.message"),
});
},
});
const handleSubmit = useCallback(
(values: z.infer<typeof validation.searchEngine.manage>) => {
mutate(values);
},
[mutate],
);
const submitButtonTranslation = useCallback((t: TranslationFunction) => t("common.action.create"), []);
return (
<SearchEngineForm
submitButtonTranslation={submitButtonTranslation}
handleSubmit={handleSubmit}
isPending={isPending}
/>
);
};
@@ -0,0 +1,21 @@
import { Stack, Title } from "@mantine/core";
import { getI18n } from "@homarr/translation/server";
import { ManageContainer } from "~/components/manage/manage-container";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { SearchEngineNewForm } from "./_search-engine-new-form";
export default async function SearchEngineNewPage() {
const t = await getI18n();
return (
<ManageContainer>
<DynamicBreadcrumb />
<Stack>
<Title>{t("search.engine.page.create.title")}</Title>
<SearchEngineNewForm />
</Stack>
</ManageContainer>
);
}
@@ -0,0 +1,132 @@
import Link from "next/link";
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
import { IconPencil, IconSearch } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
import { getI18n, getScopedI18n } from "@homarr/translation/server";
import { SearchInput, TablePagination } from "@homarr/ui";
import { z } from "@homarr/validation";
import { ManageContainer } from "~/components/manage/manage-container";
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { SearchEngineDeleteButton } from "./_search-engine-delete-button";
const searchParamsSchema = z.object({
search: z.string().optional(),
pageSize: z.string().regex(/\d+/).transform(Number).catch(10),
page: z.string().regex(/\d+/).transform(Number).catch(1),
});
type SearchParamsSchemaInputFromSchema<TSchema extends Record<string, unknown>> = Partial<{
[K in keyof TSchema]: Exclude<TSchema[K], undefined> extends unknown[] ? string[] : string;
}>;
interface SearchEnginesPageProps {
searchParams: SearchParamsSchemaInputFromSchema<z.infer<typeof searchParamsSchema>>;
}
export default async function SearchEnginesPage(props: SearchEnginesPageProps) {
const searchParams = searchParamsSchema.parse(props.searchParams);
const { items: searchEngines, totalCount } = await api.searchEngine.getPaginated(searchParams);
const tEngine = await getScopedI18n("search.engine");
return (
<ManageContainer>
<DynamicBreadcrumb />
<Stack>
<Title>{tEngine("page.list.title")}</Title>
<Group justify="space-between" align="center">
<SearchInput placeholder={`${tEngine("search")}...`} defaultValue={searchParams.search} />
<MobileAffixButton component={Link} href="/manage/search-engines/new">
{tEngine("page.create.title")}
</MobileAffixButton>
</Group>
{searchEngines.length === 0 && <SearchEngineNoResults />}
{searchEngines.length > 0 && (
<Stack gap="sm">
{searchEngines.map((searchEngine) => (
<SearchEngineCard key={searchEngine.id} searchEngine={searchEngine} />
))}
</Stack>
)}
<Group justify="end">
<TablePagination total={Math.ceil(totalCount / searchParams.pageSize)} />
</Group>
</Stack>
</ManageContainer>
);
}
interface SearchEngineCardProps {
searchEngine: RouterOutputs["searchEngine"]["getPaginated"]["items"][number];
}
const SearchEngineCard = async ({ searchEngine }: SearchEngineCardProps) => {
const t = await getScopedI18n("search.engine");
return (
<Card>
<Group justify="space-between" wrap="nowrap">
<Group align="top" justify="start" wrap="nowrap" style={{ flex: 1 }}>
<Avatar
size="sm"
src={searchEngine.iconUrl}
radius={0}
styles={{
image: {
objectFit: "contain",
},
}}
/>
<Stack gap={0}>
<Text fw={500} lineClamp={1}>
{searchEngine.name}
</Text>
{searchEngine.description && (
<Text size="sm" c="gray.6" lineClamp={4}>
{searchEngine.description}
</Text>
)}
<Anchor href={searchEngine.urlTemplate.replace("%s", "test")} lineClamp={1} size="sm">
{searchEngine.urlTemplate}
</Anchor>
</Stack>
</Group>
<Group>
<ActionIconGroup>
<ActionIcon
component={Link}
href={`/manage/search-engines/edit/${searchEngine.id}`}
variant="subtle"
color="gray"
aria-label={t("page.edit.title")}
>
<IconPencil size={16} stroke={1.5} />
</ActionIcon>
<SearchEngineDeleteButton searchEngine={searchEngine} />
</ActionIconGroup>
</Group>
</Group>
</Card>
);
};
const SearchEngineNoResults = async () => {
const t = await getI18n();
return (
<Card withBorder bg="transparent">
<Stack align="center" gap="sm">
<IconSearch size="2rem" />
<Text fw={500} size="lg">
{t("search.engine.page.list.noResults.title")}
</Text>
<Anchor href="/manage/search-engines/new">{t("search.engine.page.list.noResults.action")}</Anchor>
</Stack>
</Card>
);
};
@@ -1,17 +1,15 @@
"use client";
import type { ReactNode } from "react";
import React from "react";
import type { MantineSpacing } from "@mantine/core";
import { Card, Group, LoadingOverlay, Stack, Switch, Text, Title, UnstyledButton } from "@mantine/core";
import { Card, LoadingOverlay, Stack, Title } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import type { UseFormReturnType } from "@homarr/form";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useForm } from "@homarr/form";
import type { defaultServerSettings } from "@homarr/server-settings";
import { useScopedI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { SwitchSetting } from "~/app/[locale]/manage/settings/_components/setting-switch";
interface AnalyticsSettingsProps {
initialData: typeof defaultServerSettings.analytics;
@@ -62,6 +60,7 @@ export const AnalyticsSettings = ({ initialData }: AnalyticsSettingsProps) => {
ms="xl"
title={t("integrationData.title")}
text={t("integrationData.text")}
disabled={!form.values.enableGeneral}
/>
<SwitchSetting
form={form}
@@ -69,6 +68,7 @@ export const AnalyticsSettings = ({ initialData }: AnalyticsSettingsProps) => {
ms="xl"
title={t("widgetData.title")}
text={t("widgetData.text")}
disabled={!form.values.enableGeneral}
/>
<SwitchSetting
form={form}
@@ -76,45 +76,10 @@ export const AnalyticsSettings = ({ initialData }: AnalyticsSettingsProps) => {
ms="xl"
title={t("usersData.title")}
text={t("usersData.text")}
disabled={!form.values.enableGeneral}
/>
</Stack>
</Card>
</>
);
};
const SwitchSetting = ({
form,
ms,
title,
text,
formKey,
}: {
form: UseFormReturnType<typeof defaultServerSettings.analytics>;
formKey: keyof typeof defaultServerSettings.analytics;
ms?: MantineSpacing;
title: string;
text: ReactNode;
}) => {
const disabled = formKey !== "enableGeneral" && !form.values.enableGeneral;
const handleClick = React.useCallback(() => {
if (disabled) {
return;
}
form.setFieldValue(formKey, !form.values[formKey]);
}, [form, formKey, disabled]);
return (
<Group ms={ms} justify="space-between" gap="lg" align="center" wrap="nowrap">
<UnstyledButton style={{ flexGrow: 1 }} onClick={handleClick}>
<Stack gap={0}>
<Text fw="bold">{title}</Text>
<Text c="gray.5" fz={{ base: "xs", md: "sm" }}>
{text}
</Text>
</Stack>
</UnstyledButton>
<Switch disabled={disabled} onClick={handleClick} checked={form.values[formKey] && !disabled} />
</Group>
);
};
@@ -0,0 +1,68 @@
"use client";
import React from "react";
import { Card, LoadingOverlay, Stack, Text, Title } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useForm } from "@homarr/form";
import type { defaultServerSettings } from "@homarr/server-settings";
import { useScopedI18n } from "@homarr/translation/client";
import { SwitchSetting } from "~/app/[locale]/manage/settings/_components/setting-switch";
interface CrawlingAndIndexingSettingsProps {
initialData: typeof defaultServerSettings.crawlingAndIndexing;
}
export const CrawlingAndIndexingSettings = ({ initialData }: CrawlingAndIndexingSettingsProps) => {
const t = useScopedI18n("management.page.settings.section.crawlingAndIndexing");
const form = useForm({
initialValues: initialData,
onValuesChange: (updatedValues, _) => {
if (!form.isValid()) {
return;
}
void mutateAsync({
settingsKey: "crawlingAndIndexing",
value: updatedValues,
});
},
});
const { mutateAsync, isPending } = clientApi.serverSettings.saveSettings.useMutation({
onSettled: async () => {
await revalidatePathActionAsync("/manage/settings");
},
});
return (
<>
<Title order={2}>{t("title")}</Title>
<Card pos="relative" withBorder>
<Text c={"dimmed"} mb={"lg"}>
{t("warning")}
</Text>
<LoadingOverlay visible={isPending} zIndex={1000} overlayProps={{ radius: "sm", blur: 2 }} />
<Stack>
<SwitchSetting form={form} formKey="noIndex" title={t("noIndex.title")} text={t("noIndex.text")} />
<SwitchSetting form={form} formKey="noFollow" title={t("noFollow.title")} text={t("noFollow.text")} />
<SwitchSetting
form={form}
formKey="noTranslate"
title={t("noTranslate.title")}
text={t("noTranslate.text")}
/>
<SwitchSetting
form={form}
formKey="noSiteLinksSearchBox"
title={t("noSiteLinksSearchBox.title")}
text={t("noSiteLinksSearchBox.text")}
/>
</Stack>
</Card>
</>
);
};
@@ -0,0 +1,50 @@
import type { ReactNode } from "react";
import React from "react";
import type { MantineSpacing } from "@mantine/core";
import { Group, Stack, Switch, Text, UnstyledButton } from "@mantine/core";
import type { Modify } from "@homarr/common/types";
import type { UseFormReturnType } from "@homarr/form";
export const SwitchSetting = <TFormValue extends Record<string, boolean>>({
form,
ms,
title,
text,
formKey,
disabled,
}: {
form: Modify<
UseFormReturnType<TFormValue, () => TFormValue>,
{
setFieldValue: (key: keyof TFormValue, value: (previous: boolean) => boolean) => void;
}
>;
formKey: keyof TFormValue;
ms?: MantineSpacing;
title: string;
text: ReactNode;
disabled?: boolean;
}) => {
const handleClick = React.useCallback(() => {
if (disabled) {
return;
}
form.setFieldValue(formKey, (previous) => !previous);
}, [form, formKey, disabled]);
return (
<Group ms={ms} justify="space-between" gap="lg" align="center" wrap="nowrap">
<UnstyledButton style={{ flexGrow: 1 }} onClick={handleClick}>
<Stack gap={0}>
<Text fw="bold">{title}</Text>
<Text c="gray.5" fz={{ base: "xs", md: "sm" }}>
{text}
</Text>
</Stack>
</UnstyledButton>
<Switch disabled={disabled} onClick={handleClick} checked={form.values[formKey] && !disabled} />
</Group>
);
};
@@ -3,6 +3,7 @@ import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { getScopedI18n } from "@homarr/translation/server";
import { CrawlingAndIndexingSettings } from "~/app/[locale]/manage/settings/_components/crawling-and-indexing.settings";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { AnalyticsSettings } from "./_components/analytics.settings";
@@ -24,6 +25,7 @@ export default async function SettingsPage() {
<Stack>
<Title order={1}>{t("title")}</Title>
<AnalyticsSettings initialData={serverSettings.analytics} />
<CrawlingAndIndexingSettings initialData={serverSettings.crawlingAndIndexing} />
</Stack>
</>
);
@@ -0,0 +1,78 @@
"use client";
import { useMemo } from "react";
import { Button, Group, Stack, Text, Title } from "@mantine/core";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useModalAction } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";
import { UserAvatar } from "@homarr/ui";
import { CopyApiKeyModal } from "~/app/[locale]/manage/tools/api/components/copy-api-key-modal";
interface ApiKeysManagementProps {
apiKeys: RouterOutputs["apiKeys"]["getAll"];
}
export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => {
const { openModal } = useModalAction(CopyApiKeyModal);
const { mutate, isPending } = clientApi.apiKeys.create.useMutation({
async onSuccess(data) {
openModal({
apiKey: data.randomToken,
});
await revalidatePathActionAsync("/manage/tools/api");
},
});
const t = useScopedI18n("management.page.tool.api.tab.apiKey");
const columns = useMemo<MRT_ColumnDef<RouterOutputs["apiKeys"]["getAll"][number]>[]>(
() => [
{
accessorKey: "id",
header: t("table.header.id"),
},
{
accessorKey: "user",
header: t("table.header.createdBy"),
Cell: ({ row }) => (
<Group gap={"xs"}>
<UserAvatar user={row.original.user} size={"sm"} />
<Text>{row.original.user.name}</Text>
</Group>
),
},
],
[t],
);
const table = useMantineReactTable({
columns,
data: apiKeys,
renderTopToolbarCustomActions: () => (
<Button
onClick={() => {
mutate();
}}
loading={isPending}
>
{t("button.createApiToken")}
</Button>
),
enableDensityToggle: false,
state: {
density: "xs",
},
});
return (
<Stack>
<Title>{t("title")}</Title>
<MantineReactTable table={table} />
</Stack>
);
};
@@ -0,0 +1,34 @@
import { Button, CopyButton, PasswordInput, Stack, Text } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { createModal } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";
export const CopyApiKeyModal = createModal<{ apiKey: string }>(({ actions, innerProps }) => {
const t = useScopedI18n("management.page.tool.api.modal.createApiToken");
const [visible, { toggle }] = useDisclosure(false);
return (
<Stack>
<Text>{t("description")}</Text>
<PasswordInput value={innerProps.apiKey} visible={visible} onVisibilityChange={toggle} readOnly />
<CopyButton value={innerProps.apiKey}>
{({ copy }) => (
<Button
onClick={() => {
copy();
actions.closeModal();
}}
variant="default"
fullWidth
>
{t("button")}
</Button>
)}
</CopyButton>
</Stack>
);
}).withOptions({
defaultTitle(t) {
return t("management.page.tool.api.modal.createApiToken.title");
},
});
@@ -0,0 +1,23 @@
"use client";
import type { OpenAPIV3 } from "openapi-types";
import SwaggerUI from "swagger-ui-react";
// workaround for CSS that cannot be processed by next.js, https://github.com/swagger-api/swagger-ui/issues/10045
import "../swagger-ui-dark.css";
import "../swagger-ui-overrides.css";
import "../swagger-ui.css";
interface SwaggerUIClientProps {
document: OpenAPIV3.Document;
}
export const SwaggerUIClient = ({ document }: SwaggerUIClientProps) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const requestInterceptor = (req: Record<string, any>) => {
req.credentials = "omit";
return req;
};
return <SwaggerUI requestInterceptor={requestInterceptor} spec={document} />;
};
@@ -0,0 +1,53 @@
import { headers } from "next/headers";
import { notFound } from "next/navigation";
import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from "@mantine/core";
import { openApiDocument } from "@homarr/api";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { extractBaseUrlFromHeaders } from "@homarr/common";
import { getScopedI18n } from "@homarr/translation/server";
import { SwaggerUIClient } from "~/app/[locale]/manage/tools/api/components/swagger-ui";
import { createMetaTitle } from "~/metadata";
import { ApiKeysManagement } from "./components/api-keys";
export async function generateMetadata() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
return {};
}
const t = await getScopedI18n("management");
return {
title: createMetaTitle(t("metaTitle")),
};
}
export default async function ApiPage() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
notFound();
}
const document = openApiDocument(extractBaseUrlFromHeaders(headers()));
const apiKeys = await api.apiKeys.getAll();
const t = await getScopedI18n("management.page.tool.api.tab");
return (
<Stack>
<Tabs defaultValue={"documentation"}>
<TabsList>
<TabsTab value={"documentation"}>{t("documentation.label")}</TabsTab>
<TabsTab value={"authentication"}>{t("apiKey.label")}</TabsTab>
</TabsList>
<TabsPanel value={"authentication"}>
<ApiKeysManagement apiKeys={apiKeys} />
</TabsPanel>
<TabsPanel value={"documentation"}>
<SwaggerUIClient document={document} />
</TabsPanel>
</Tabs>
</Stack>
);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,4 @@
.swagger-ui .info {
margin: 0 !important;
margin-bottom: 20px !important;
}
File diff suppressed because it is too large. Load diff
@@ -2,9 +2,9 @@
import type { MantineColor } from "@mantine/core";
import { Avatar, Badge, Box, Button, Group, Text } from "@mantine/core";
import { IconPlayerPlay, IconPlayerStop, IconRotateClockwise, IconTrash } from "@tabler/icons-react";
import { IconPlayerPlay, IconPlayerStop, IconRefresh, IconRotateClockwise, IconTrash } from "@tabler/icons-react";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -15,6 +15,7 @@ import type { TranslationFunction } from "@homarr/translation";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { TablerIcon } from "@homarr/ui";
import { OverflowBadge } from "@homarr/ui";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
const createColumns = (
t: TranslationFunction,
@@ -45,10 +46,12 @@ const createColumns = (
accessorKey: "image",
header: t("docker.field.containerImage.label"),
maxSize: 200,
Cell({ renderedCellValue }) {
Cell({ renderedCellValue, cell }) {
return (
<Box maw={200}>
<Text truncate="end">{renderedCellValue}</Text>
<Text truncate="end" title={cell.row.original.image}>
{renderedCellValue}
</Text>
</Box>
);
},
@@ -74,7 +77,7 @@ export function DockerTable(initialData: RouterOutputs["docker"]["getContainers"
refetchOnReconnect: false,
});
const relativeTime = useTimeAgo(data.timestamp);
const table = useMantineReactTable({
const table = useTranslatedMantineReactTable({
data: data.containers,
enableDensityToggle: false,
enableColumnActions: false,
@@ -92,6 +95,35 @@ export function DockerTable(initialData: RouterOutputs["docker"]["getContainers"
},
initialState: { density: "xs", showGlobalFilter: true },
renderTopToolbarCustomActions: () => {
const utils = clientApi.useUtils();
const { mutate, isPending } = clientApi.docker.invalidate.useMutation({
async onSuccess() {
await utils.docker.getContainers.invalidate();
showSuccessNotification({
title: tDocker("action.refresh.notification.success.title"),
message: tDocker("action.refresh.notification.success.message"),
});
},
onError() {
showErrorNotification({
title: tDocker("action.refresh.notification.error.title"),
message: tDocker("action.refresh.notification.error.message"),
});
},
});
return (
<Button
variant="default"
rightSection={<IconRefresh size="1rem" />}
onClick={() => mutate()}
loading={isPending}
>
{tDocker("action.refresh.label")}
</Button>
);
},
renderToolbarAlertBannerContent: ({ groupedAlert, table }) => {
return (
<Group gap={"sm"}>
@@ -1,12 +1,19 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { getScopedI18n } from "@homarr/translation/server";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { DockerTable } from "./docker-table";
export default async function DockerPage() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
notFound();
}
const { containers, timestamp } = await api.docker.getContainers();
const tDocker = await getScopedI18n("docker");
@@ -0,0 +1,10 @@
"use client";
import dynamic from "next/dynamic";
export const ClientSideTerminalComponent = dynamic(
() => import("./terminal").then(({ TerminalComponent }) => TerminalComponent),
{
ssr: false,
},
);
@@ -4,13 +4,20 @@ import { getScopedI18n } from "@homarr/translation/server";
import "@xterm/xterm/css/xterm.css";
import dynamic from "next/dynamic";
import { notFound } from "next/navigation";
import { auth } from "@homarr/auth/next";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
import { createMetaTitle } from "~/metadata";
import { ClientSideTerminalComponent } from "./client";
export async function generateMetadata() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
return {};
}
const t = await getScopedI18n("management");
return {
@@ -18,11 +25,12 @@ export async function generateMetadata() {
};
}
const ClientSideTerminalComponent = dynamic(() => import("./terminal"), {
ssr: false,
});
export default async function LogsManagementPage() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
notFound();
}
export default function LogsManagementPage() {
return (
<>
<DynamicBreadcrumb />
@@ -10,7 +10,7 @@ import { clientApi } from "@homarr/api/client";
import classes from "./terminal.module.css";
export default function TerminalComponent() {
export const TerminalComponent = () => {
const ref = useRef<HTMLDivElement>(null);
const terminalRef = useRef<Terminal>();
@@ -54,4 +54,4 @@ export default function TerminalComponent() {
};
}, []);
return <Box ref={ref} id="terminal" className={classes.outerTerminal} h="100%"></Box>;
}
};
@@ -58,7 +58,7 @@ export const JobsList = ({ initialJobs }: JobsListProps) => {
<Group justify={"space-between"} gap={"md"}>
<Stack gap={0}>
<Group>
<Text>{t(`${job.job.name}.label` as TranslationKeys)}</Text>
<Text>{t(`job.${job.job.name}.label` as TranslationKeys)}</Text>
{job.status?.status === "idle" && <Badge variant="default">{t("status.idle")}</Badge>}
{job.status?.status === "running" && <Badge color="green">{t("status.running")}</Badge>}
{job.status?.lastExecutionStatus === "error" && <Badge color="red">{t("status.error")}</Badge>}
@@ -1,12 +1,18 @@
import { notFound } from "next/navigation";
import { Box, Title } from "@mantine/core";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { getScopedI18n } from "@homarr/translation/server";
import { createMetaTitle } from "~/metadata";
import { JobsList } from "./_components/jobs-list";
export async function generateMetadata() {
const session = await auth();
if (!session?.user.permissions.includes("admin")) {
return {};
}
const t = await getScopedI18n("management");
return {
@@ -15,6 +21,11 @@ export async function generateMetadata() {
}
export default async function TasksPage() {
const session = await auth();
if (!session?.user.permissions.includes("admin")) {
notFound();
}
const jobs = await api.cronJobs.getJobs();
return (
<Box>
@@ -0,0 +1,67 @@
"use client";
import { Button, Group, Select, Stack } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
interface ChangeHomeBoardFormProps {
user: RouterOutputs["user"]["getById"];
boardsData: { value: string; label: string }[];
}
export const ChangeHomeBoardForm = ({ user, boardsData }: ChangeHomeBoardFormProps) => {
const t = useI18n();
const { mutate, isPending } = clientApi.user.changeHomeBoardId.useMutation({
async onSettled() {
await revalidatePathActionAsync(`/manage/users/${user.id}`);
},
onSuccess(_, variables) {
form.setInitialValues({
homeBoardId: variables.homeBoardId,
});
showSuccessNotification({
message: t("user.action.changeHomeBoard.notification.success.message"),
});
},
onError() {
showErrorNotification({
message: t("user.action.changeHomeBoard.notification.error.message"),
});
},
});
const form = useZodForm(validation.user.changeHomeBoard, {
initialValues: {
homeBoardId: user.homeBoardId ?? "",
},
});
const handleSubmit = (values: FormType) => {
mutate({
userId: user.id,
...values,
});
};
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="md">
<Select w="100%" data={boardsData} {...form.getInputProps("homeBoardId")} />
<Group justify="end">
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
</Stack>
</form>
);
};
type FormType = z.infer<typeof validation.user.changeHomeBoard>;
@@ -6,11 +6,10 @@ import { Button } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { useI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface DeleteUserButtonProps {
user: RouterOutputs["user"]["getById"];
}
@@ -0,0 +1,82 @@
"use client";
import { Button, Group, Radio, Stack } from "@mantine/core";
import dayjs from "dayjs";
import localeData from "dayjs/plugin/localeData";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
dayjs.extend(localeData);
interface FirstDayOfWeekProps {
user: RouterOutputs["user"]["getById"];
}
const weekDays = dayjs.weekdays(false);
export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
const t = useI18n();
const { mutate, isPending } = clientApi.user.changeFirstDayOfWeek.useMutation({
async onSettled() {
await revalidatePathActionAsync(`/manage/users/${user.id}`);
},
onSuccess(_, variables) {
form.setInitialValues({
firstDayOfWeek: variables.firstDayOfWeek,
});
showSuccessNotification({
message: t("user.action.changeFirstDayOfWeek.notification.success.message"),
});
},
onError() {
showErrorNotification({
message: t("user.action.changeFirstDayOfWeek.notification.error.message"),
});
},
});
const form = useZodForm(validation.user.firstDayOfWeek, {
initialValues: {
firstDayOfWeek: user.firstDayOfWeek,
},
});
const handleSubmit = (values: FormType) => {
mutate({
id: user.id,
...values,
});
};
const inputProps = form.getInputProps("firstDayOfWeek");
const onChange = inputProps.onChange as (value: number) => void;
const value = (inputProps.value as number).toString();
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="md">
<Radio.Group {...inputProps} value={value} onChange={(value) => onChange(parseInt(value))}>
<Group mt="xs">
<Radio value="1" label={weekDays[1]} />
<Radio value="6" label={weekDays[6]} />
<Radio value="0" label={weekDays[0]} />
</Group>
</Radio.Group>
<Group justify="end">
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
</Stack>
</form>
);
};
type FormType = z.infer<typeof validation.user.firstDayOfWeek>;
@@ -0,0 +1,66 @@
"use client";
import { Button, Group, Stack, Switch } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
interface PingIconsEnabledProps {
user: RouterOutputs["user"]["getById"];
}
export const PingIconsEnabled = ({ user }: PingIconsEnabledProps) => {
const t = useI18n();
const { mutate, isPending } = clientApi.user.changePingIconsEnabled.useMutation({
async onSettled() {
await revalidatePathActionAsync(`/manage/users/${user.id}`);
},
onSuccess(_, variables) {
form.setInitialValues({
pingIconsEnabled: variables.pingIconsEnabled,
});
showSuccessNotification({
message: t("user.action.changePingIconsEnabled.notification.success.message"),
});
},
onError() {
showErrorNotification({
message: t("user.action.changePingIconsEnabled.notification.error.message"),
});
},
});
const form = useZodForm(validation.user.pingIconsEnabled, {
initialValues: {
pingIconsEnabled: user.pingIconsEnabled,
},
});
const handleSubmit = (values: FormType) => {
mutate({
id: user.id,
...values,
});
};
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="md">
<Switch {...form.getInputProps("pingIconsEnabled")} label={t("user.field.pingIconsEnabled.label")} />
<Group justify="end">
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
</Stack>
</form>
);
};
type FormType = z.infer<typeof validation.user.pingIconsEnabled>;
@@ -7,13 +7,12 @@ import { IconPencil, IconPhotoEdit, IconPhotoX } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { UserAvatar } from "@homarr/ui";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface UserProfileAvatarForm {
user: RouterOutputs["user"]["getById"];
}
@@ -93,24 +92,38 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
});
}, [mutate, user.id, openConfirmModal, tManageAvatar]);
const isCredentialsUser = user.provider === "credentials";
return (
<Box pos="relative">
<Menu opened={opened} keepMounted onChange={toggle} position="bottom-start" withArrow>
<Menu
opened={opened}
keepMounted
onChange={isCredentialsUser ? toggle : undefined}
position="bottom-start"
withArrow
>
<Menu.Target>
<UnstyledButton onClick={toggle}>
<UnstyledButton
component={isCredentialsUser ? undefined : "div"}
style={{ cursor: !isCredentialsUser ? "default" : undefined }}
onClick={isCredentialsUser ? toggle : undefined}
>
<UserAvatar user={user} size={200} />
<Button
component="div"
pos="absolute"
bottom={0}
left={0}
size="compact-md"
fw="normal"
variant="default"
leftSection={<IconPencil size={18} stroke={1.5} />}
>
{t("common.action.edit")}
</Button>
{isCredentialsUser && (
<Button
component="div"
pos="absolute"
bottom={0}
left={0}
size="compact-md"
fw="normal"
variant="default"
leftSection={<IconPencil size={18} stroke={1.5} />}
>
{t("common.action.edit")}
</Button>
)}
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
@@ -5,13 +5,12 @@ import { Button, Group, Stack, TextInput } from "@mantine/core";
import type { RouterInputs, RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { validation } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface UserProfileFormProps {
user: RouterOutputs["user"]["getById"];
}
@@ -51,27 +50,42 @@ export const UserProfileForm = ({ user }: UserProfileFormProps) => {
},
});
// Only credentials users can edit their profile
const isProviderCredentials = user.provider === "credentials";
const handleSubmit = useCallback(
(values: FormType) => {
if (!isProviderCredentials) return;
mutate({
...values,
id: user.id,
});
},
[user.id, mutate],
[isProviderCredentials, mutate, user.id],
);
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<TextInput label={t("user.field.username.label")} withAsterisk {...form.getInputProps("name")} />
<TextInput label={t("user.field.email.label")} {...form.getInputProps("email")} />
<TextInput
disabled={!isProviderCredentials}
label={t("user.field.username.label")}
withAsterisk
{...form.getInputProps("name")}
/>
<TextInput
disabled={!isProviderCredentials}
label={t("user.field.email.label")}
{...form.getInputProps("email")}
/>
<Group justify="end">
<Button type="submit" color="teal" disabled={!form.isDirty()} loading={isPending}>
{t("common.action.saveChanges")}
</Button>
</Group>
{isProviderCredentials && (
<Group justify="end">
<Button type="submit" color="teal" disabled={!form.isDirty()} loading={isPending}>
{t("common.action.saveChanges")}
</Button>
</Group>
)}
</Stack>
</form>
);
@@ -1,12 +0,0 @@
import { Stack, Title } from "@mantine/core";
import { LanguageCombobox } from "~/components/language/language-combobox";
export const ProfileLanguageChange = () => {
return (
<Stack mb="lg">
<Title order={2}>Language & Region</Title>
<LanguageCombobox />
</Stack>
);
};
@@ -1,18 +1,22 @@
import { notFound } from "next/navigation";
import { Box, Group, Stack, Title } from "@mantine/core";
import { Alert, Box, Group, Stack, Title } from "@mantine/core";
import { IconExclamationCircle } from "@tabler/icons-react";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { getI18n, getScopedI18n } from "@homarr/translation/server";
import { LanguageCombobox } from "~/components/language/language-combobox";
import { DangerZoneItem, DangerZoneRoot } from "~/components/manage/danger-zone";
import { catchTrpcNotFound } from "~/errors/trpc-not-found";
import { createMetaTitle } from "~/metadata";
import { canAccessUserEditPage } from "../access";
import { ChangeHomeBoardForm } from "./_components/_change-home-board";
import { DeleteUserButton } from "./_components/_delete-user-button";
import { FirstDayOfWeek } from "./_components/_first-day-of-week";
import { PingIconsEnabled } from "./_components/_ping-icons-enabled";
import { UserProfileAvatarForm } from "./_components/_profile-avatar-form";
import { UserProfileForm } from "./_components/_profile-form";
import { ProfileLanguageChange } from "./_components/_profile-language-change";
interface Props {
params: {
@@ -53,8 +57,18 @@ export default async function EditUserPage({ params }: Props) {
notFound();
}
const boards = await api.board.getAllBoards();
const isCredentialsUser = user.provider === "credentials";
return (
<Stack>
{!isCredentialsUser && (
<Alert variant="light" color="yellow" icon={<IconExclamationCircle size="1rem" stroke={1.5} />}>
{t("management.page.user.fieldsDisabledExternalProvider")}
</Alert>
)}
<Title>{tGeneral("title")}</Title>
<Group gap="xl">
<Box flex={1}>
@@ -65,15 +79,41 @@ export default async function EditUserPage({ params }: Props) {
</Box>
</Group>
<ProfileLanguageChange />
<Stack mb="lg">
<Title order={2}>{tGeneral("item.language")}</Title>
<LanguageCombobox />
</Stack>
<DangerZoneRoot>
<DangerZoneItem
label={t("user.action.delete.label")}
description={t("user.action.delete.description")}
action={<DeleteUserButton user={user} />}
<Stack mb="lg">
<Title order={2}>{tGeneral("item.board")}</Title>
<ChangeHomeBoardForm
user={user}
boardsData={boards.map((board) => ({
value: board.id,
label: board.name,
}))}
/>
</DangerZoneRoot>
</Stack>
<Stack mb="lg">
<Title order={2}>{tGeneral("item.firstDayOfWeek")}</Title>
<FirstDayOfWeek user={user} />
</Stack>
<Stack mb="lg">
<Title order={2}>{tGeneral("item.accessibility")}</Title>
<PingIconsEnabled user={user} />
</Stack>
{isCredentialsUser && (
<DangerZoneRoot>
<DangerZoneItem
label={t("user.action.delete.label")}
description={t("user.action.delete.description")}
action={<DeleteUserButton user={user} />}
/>
</DangerZoneRoot>
)}
</Stack>
);
}
@@ -28,6 +28,8 @@ export default async function Layout({ children, params }: PropsWithChildren<Lay
notFound();
}
const isCredentialsUser = user.provider === "credentials";
return (
<ManageContainer size="xl">
<DynamicBreadcrumb
@@ -41,7 +43,7 @@ export default async function Layout({ children, params }: PropsWithChildren<Lay
/>
<Grid>
<GridCol span={12}>
<Group justify="space-between" align="center">
<Group align="center">
<UserAvatar user={user} size="lg" />
<Stack gap={0}>
<Title order={3}>{user.name}</Title>
@@ -57,11 +59,13 @@ export default async function Layout({ children, params }: PropsWithChildren<Lay
label={tUser("setting.general.title")}
icon={<IconSettings size="1rem" stroke={1.5} />}
/>
<NavigationLink
href={`/manage/users/${params.userId}/security`}
label={tUser("setting.security.title")}
icon={<IconShieldLock size="1rem" stroke={1.5} />}
/>
{isCredentialsUser && (
<NavigationLink
href={`/manage/users/${params.userId}/security`}
label={tUser("setting.security.title")}
icon={<IconShieldLock size="1rem" stroke={1.5} />}
/>
)}
</Stack>
</Stack>
</GridCol>
@@ -5,13 +5,13 @@ import { Button, Fieldset, Group, PasswordInput, Stack } from "@mantine/core";
import type { RouterInputs, RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { CustomPasswordInput } from "@homarr/ui";
import { validation } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface ChangePasswordFormProps {
user: RouterOutputs["user"]["getById"];
}
@@ -71,7 +71,12 @@ export const ChangePasswordForm = ({ user }: ChangePasswordFormProps) => {
/>
)}
<PasswordInput withAsterisk label={t("user.field.password.label")} {...form.getInputProps("password")} />
<CustomPasswordInput
withPasswordRequirements
withAsterisk
label={t("user.field.password.label")}
{...form.getInputProps("password")}
/>
<PasswordInput
withAsterisk
@@ -28,6 +28,10 @@ export default async function UserSecurityPage({ params }: Props) {
notFound();
}
if (user.provider !== "credentials") {
notFound();
}
return (
<Stack>
<Title>{tSecurity("title")}</Title>
@@ -2,20 +2,23 @@
import { useMemo } from "react";
import Link from "next/link";
import { Avatar, Button, Group, Text, ThemeIcon, Title } from "@mantine/core";
import { Anchor, Button, Group, Text, Title, Tooltip } from "@mantine/core";
import { IconCheck } from "@tabler/icons-react";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { UserAvatar } from "@homarr/ui";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
interface UserListComponentProps {
initialUserList: RouterOutputs["user"]["getAll"];
credentialsProviderEnabled: boolean;
}
export const UserListComponent = ({ initialUserList }: UserListComponentProps) => {
export const UserListComponent = ({ initialUserList, credentialsProviderEnabled }: UserListComponentProps) => {
const tUserList = useScopedI18n("management.page.user.list");
const t = useI18n();
const { data, isLoading } = clientApi.user.getAll.useQuery(undefined, {
@@ -27,26 +30,27 @@ export const UserListComponent = ({ initialUserList }: UserListComponentProps) =
{
accessorKey: "name",
header: t("user.field.username.label"),
grow: 100,
grow: 1,
Cell: ({ renderedCellValue, row }) => (
<Link href={`/manage/users/${row.original.id}/general`}>
<Group>
<Avatar size="sm"></Avatar>
<Group>
<UserAvatar size="sm" user={row.original} />
<Anchor component={Link} href={`/manage/users/${row.original.id}/general`}>
{renderedCellValue}
</Group>
</Link>
</Anchor>
</Group>
),
},
{
accessorKey: "email",
header: t("user.field.email.label"),
size: 300,
Cell: ({ renderedCellValue, row }) => (
<Group>
<Group wrap="nowrap" gap="sm">
{row.original.email ? renderedCellValue : <Text>-</Text>}
{row.original.emailVerified && (
<ThemeIcon radius="xl" size="sm">
<IconCheck size="1rem" />
</ThemeIcon>
<Tooltip label={t("user.field.email.verified")} position="top">
<IconCheck color="var(--mantine-color-green-4)" size="1rem" />
</Tooltip>
)}
</Group>
),
@@ -55,7 +59,7 @@ export const UserListComponent = ({ initialUserList }: UserListComponentProps) =
[t],
);
const table = useMantineReactTable({
const table = useTranslatedMantineReactTable({
columns,
data,
enableRowSelection: true,
@@ -66,11 +70,12 @@ export const UserListComponent = ({ initialUserList }: UserListComponentProps) =
enableFullScreenToggle: false,
layoutMode: "grid-no-grow",
getRowId: (row) => row.id,
renderTopToolbarCustomActions: () => (
<Button component={Link} href="/manage/users/create">
Create New User
</Button>
),
renderTopToolbarCustomActions: () =>
credentialsProviderEnabled ? (
<Button component={Link} href="/manage/users/create">
{t("management.page.user.create.title")}
</Button>
) : null,
state: {
isLoading,
},
@@ -1,17 +1,18 @@
"use client";
import { useCallback, useMemo, useState } from "react";
import { Avatar, Card, PasswordInput, Stack, Stepper, Text, TextInput, Title } from "@mantine/core";
import { Card, PasswordInput, Stack, Stepper, Text, TextInput, Title } from "@mantine/core";
import { IconUserCheck } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { CustomPasswordInput, UserAvatar } from "@homarr/ui";
import { validation, z } from "@homarr/validation";
import { createCustomErrorParams } from "@homarr/validation/form";
import { StepperNavigationComponent } from "./stepper-navigation.component";
import { StepperNavigationComponent } from "./stepper-navigation";
export const UserCreateStepperComponent = () => {
const t = useScopedI18n("management.page.user.create");
@@ -123,7 +124,8 @@ export const UserCreateStepperComponent = () => {
<form>
<Card p="xl">
<Stack gap="md">
<PasswordInput
<CustomPasswordInput
withPasswordRequirements
label={tUserField("password.label")}
variant="filled"
withAsterisk
@@ -150,7 +152,7 @@ export const UserCreateStepperComponent = () => {
<Stepper.Step label={t("step.review.label")} allowStepSelect={false} allowStepClick={false}>
<Card p="xl">
<Stack maw={300} align="center" mx="auto">
<Avatar size="xl">{generalForm.values.username}</Avatar>
<UserAvatar size="xl" user={{ name: generalForm.values.username, image: null }} />
<Text tt="uppercase" fw="bolder" size="xl">
{generalForm.values.username}
</Text>
@@ -1,3 +1,7 @@
import { notFound } from "next/navigation";
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { getScopedI18n } from "@homarr/translation/server";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
@@ -5,6 +9,13 @@ import { createMetaTitle } from "~/metadata";
import { UserCreateStepperComponent } from "./_components/create-user-stepper";
export async function generateMetadata() {
if (!isProviderEnabled("credentials")) return {};
const session = await auth();
if (!session?.user.permissions.includes("admin")) {
return {};
}
const t = await getScopedI18n("management.page.user.create");
return {
@@ -12,7 +23,16 @@ export async function generateMetadata() {
};
}
export default function CreateUserPage() {
export default async function CreateUserPage() {
if (!isProviderEnabled("credentials")) {
notFound();
}
const session = await auth();
if (!session?.user.permissions.includes("admin")) {
return notFound();
}
return (
<>
<DynamicBreadcrumb />
@@ -5,12 +5,11 @@ import { useRouter } from "next/navigation";
import { Button } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface DeleteGroupProps {
group: {
id: string;
@@ -4,21 +4,21 @@ import { useCallback } from "react";
import { Button, Group, Stack, TextInput } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { validation } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
interface RenameGroupFormProps {
group: {
id: string;
name: string;
};
disabled?: boolean;
}
export const RenameGroupForm = ({ group }: RenameGroupFormProps) => {
export const RenameGroupForm = ({ group, disabled }: RenameGroupFormProps) => {
const t = useI18n();
const { mutate, isPending } = clientApi.group.updateGroup.useMutation();
const form = useZodForm(validation.group.update.pick({ name: true }), {
@@ -29,6 +29,9 @@ export const RenameGroupForm = ({ group }: RenameGroupFormProps) => {
const handleSubmit = useCallback(
(values: FormType) => {
if (disabled) {
return;
}
mutate(
{
...values,
@@ -61,13 +64,15 @@ export const RenameGroupForm = ({ group }: RenameGroupFormProps) => {
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<TextInput label={t("group.field.name")} {...form.getInputProps("name")} />
<TextInput label={t("group.field.name")} {...form.getInputProps("name")} disabled={disabled} />
<Group justify="end">
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.saveChanges")}
</Button>
</Group>
{!disabled && (
<Group justify="end">
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.saveChanges")}
</Button>
</Group>
)}
</Stack>
</form>
);
@@ -0,0 +1,27 @@
import Link from "next/link";
import { Alert, Anchor } from "@mantine/core";
import { IconExclamationCircle } from "@tabler/icons-react";
import { createDocumentationLink } from "@homarr/definitions";
import { getI18n } from "@homarr/translation/server";
export const ReservedGroupAlert = async () => {
const t = await getI18n();
return (
<Alert variant="light" color="yellow" icon={<IconExclamationCircle size="1rem" stroke={1.5} />}>
{t("group.reservedNotice.message", {
checkoutDocs: (
<Anchor
size="sm"
component={Link}
href={createDocumentationLink("/docs/management/users", "#special-groups")}
target="_blank"
>
{t("common.action.checkoutDocs")}
</Anchor>
),
})}
</Alert>
);
};
@@ -8,7 +8,7 @@ import { useConfirmModal, useModalAction } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { UserSelectModal } from "~/app/[locale]/boards/[name]/settings/_access/user-select-modal";
import { UserSelectModal } from "~/components/access/user-select-modal";
interface TransferGroupOwnershipProps {
group: {
Loaded 100 of 624 files, more files were not shown because too many files have changed in this diff. Show more