Compare commits

..
Author SHA1 Message Date
Manuel 475d27baa0 feat: add websocket banner 2025-03-28 22:19:32 +01:00
727 changed files with 12683 additions and 65272 deletions

No files matched your search

-1
View File
@@ -1,7 +1,6 @@
Dockerfile
.dockerignore
node_modules
**/node_modules
npm-debug.log
README.md
.next
+2 -10
View File
@@ -12,10 +12,6 @@ AUTH_SECRET="supersecret"
# or starting the project without any (which will show a randomly generated one).
SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
# Cron job API key is generated every time the container starts as it is required for communication between nextjs-api and tasks-api
# export CRON_JOB_API_KEY=$(openssl rand -base64 32)
# CRON_JOB_API_KEY="your-generated-api-key"
LOG_LEVEL='info'
# This is how you can use the sqlite driver:
@@ -34,15 +30,11 @@ DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE'
# DB_PASSWORD='password'
# DB_NAME='name-of-database'
# The below path can be used to store trusted certificates, it is not required and can be left empty.
# If it is empty, it will default to `/appdata/trusted-certificates` in production.
# The below path can be used to store trusted certificates during development, it is not required and can be left empty.
# If it is used, please use the full path to the directory where the certificates are stored.
# LOCAL_CERTIFICATE_PATH='FULL_PATH_TO_CERTIFICATES'
TURBO_TELEMETRY_DISABLED=1
# Enable kubernetes tool
# ENABLE_KUBERNETES=true
# Enable mock integration
UNSAFE_ENABLE_MOCK_INTEGRATION=true
# ENABLE_KUBERNETES=true
-27
View File
@@ -31,33 +31,6 @@ body:
label: Version
description: What version of Homarr are you running?
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
- 1.33.0
- 1.32.0
- 1.31.0
- 1.30.1
- 1.30.0
- 1.29.0
- 1.28.1
- 1.28.0
- 1.27.0
- 1.26.0
- 1.25.0
- 1.24.0
- 1.23.0
- 1.22.0
- 1.21.0
- 1.20.0
- 1.19.1
- 1.19.0
- 1.18.0
- 1.17.0
- 1.16.0
- 1.15.0
- 1.14.0
- 1.13.1
- 1.13.0
- 1.12.0
- 1.11.0
- 1.10.0
+4 -4
View File
@@ -6,8 +6,8 @@
**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**
- [ ] Builds without warnings or errors (`pnpm build`, autofix with `pnpm format:fix`)
- [ ] Pull request targets `dev` branch
- [ ] Builds without warnings or errors (``pnpm build``, autofix with ``pnpm format:fix``)
- [ ] Pull request targets ``dev`` branch
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
- [ ] No shorthand variable names are used (eg. `x`, `y`, `i` or any abbrevation)
- [ ] Documentation is up to date. Create a pull request [here](https://github.com/homarr-labs/documentation/).
- [ ] No shorthand variable names are used (eg. ``x``, ``y``, ``i`` or any abbrevation)
+2 -2
View File
@@ -6,10 +6,10 @@ on:
jobs:
approve-automatic-prs:
runs-on: ubuntu-latest
if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 # Id of renovate bot and crowdin bot see https://api.github.com/users/homarr-renovate%5Bbot%5D and https://api.github.com/users/homarr-crowdin%5Bbot%5D and https://api.github.com/users/homarr-update-contributors%5Bbot%5D
if: github.actor_id == 158783068 || github.actor_id == 190541745 # Id of renovate bot and crowdin bot see https://api.github.com/users/homarr-renovate%5Bbot%5D and https://api.github.com/users/homarr-crowdin%5Bbot%5D
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Obtain token
id: obtainToken
uses: tibdex/github-app-token@v2
+6 -6
View File
@@ -22,7 +22,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup
uses: ./tooling/github/setup
@@ -37,7 +37,7 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup
uses: ./tooling/github/setup
@@ -48,7 +48,7 @@ jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup
uses: ./tooling/github/setup
@@ -59,7 +59,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup
uses: ./tooling/github/setup
@@ -77,7 +77,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup
uses: ./tooling/github/setup
- name: Build docker image
@@ -100,7 +100,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup
uses: ./tooling/github/setup
- name: Copy env
@@ -0,0 +1,14 @@
# https://github.com/webiny/action-conventional-commits?tab=readme-ov-file
name: "[Conventions] Semantic Commits"
on:
pull_request:
branches: [ dev ]
jobs:
build:
name: Conventional Commits
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: webiny/action-conventional-commits@v1.3.0
@@ -14,6 +14,6 @@ jobs:
validate-pull-request-title:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v6
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Obtain token
id: obtainToken
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Upload Crowdin translations
uses: crowdin/github-action@v2
@@ -53,14 +53,14 @@ jobs:
with:
private_key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app_id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
- uses: actions/checkout@v5
- uses: actions/checkout@v4
if: env.SKIP_RELEASE == 'false'
with:
persist-credentials: false
- uses: actions/setup-node@v4
if: env.SKIP_RELEASE == 'false'
with:
node-version: 22.18.0
node-version: 22
- run: npm i -g pnpm
if: env.SKIP_RELEASE == 'false'
- name: Install dependencies
@@ -110,7 +110,7 @@ jobs:
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.git_ref }}
@@ -149,7 +149,7 @@ jobs:
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.git_ref }}
@@ -27,7 +27,7 @@ jobs:
with:
args: "Automatic release has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Next Version
-82
View File
@@ -1,82 +0,0 @@
permissions: {}
on:
release:
types: [published]
jobs:
trigger-docs-release:
name: Trigger Documentation Release
runs-on: ubuntu-latest
steps:
- name: Obtain token
id: obtainToken
uses: tibdex/github-app-token@v2
with:
private_key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
app_id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
installation_retrieval_mode: repository
installation_retrieval_payload: homarr-labs/documentation
- name: Trigger documentation release
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
SOURCE_TAG: ${{ github.event.release.tag_name }}
run: |
curl -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/homarr-labs/documentation/dispatches \
-d @- <<EOF
{
"event_type": "trigger-release",
"client_payload": {
"tag": "${SOURCE_TAG}"
}
}
EOF
update-bug-report-template:
name: Update Bug Report Template
runs-on: ubuntu-latest
steps:
- name: Obtain token
id: obtainToken
uses: tibdex/github-app-token@v2
with:
private_key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app_id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
- name: Checkout code
uses: actions/checkout@v5
with:
token: ${{ steps.obtainToken.outputs.token }}
- name: Setup
uses: ./tooling/github/setup
- run: pnpm run scripts:update-bug-report-template
env:
NEXT_VERSION: ${{ github.event.release.tag_name }}
- name: Commit changes
run: |
git config --global user.email "175486441+homarr-releases[bot]@users.noreply.github.com"
git config --global user.name "Releases Homarr"
git add .
git commit -m "chore: update bug report template"
- name: Create Pull Request
id: create-pull-request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.obtainToken.outputs.token }}
branch: update-bug-report-template
base: dev
title: "chore: update bug report template"
delete-branch: true
body: |
This PR automatically updates the bug report template of Homarr in the .github/ISSUE_TEMPLATE/bug_report.yml
- name: Install GitHub CLI
run: sudo apt-get install -y gh
- name: Enable auto-merge
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
gh pr merge ${{steps.create-pull-request.outputs.pull-request-number}} --auto --squash
-70
View File
@@ -1,70 +0,0 @@
name: Update Contributors
on:
schedule:
- cron: "0 12 * * FRI" # At 12:00 on Friday.
workflow_dispatch:
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
CROWDIN_TOKEN: "${{ secrets.CROWDIN_UPDATE_CONTRIBUTORS_TOKEN }}"
permissions:
contents: write
jobs:
update-contributors:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22]
steps:
- name: Obtain token
id: obtainToken
uses: tibdex/github-app-token@v2
with:
private_key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
app_id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
- name: Checkout repository
uses: actions/checkout@v5
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Run update script
run: node ./scripts/update-contributors.mjs
- name: Commit changes
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
git config --global user.email "210161987+homarr-update-contributors[bot]@users.noreply.github.com"
git config --global user.name "Homarr Update Contributors"
git add .
git commit -m "chore: update contributors"
- name: Create Pull Request
id: create-pull-request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.obtainToken.outputs.token }}
branch: update-contributors
base: dev
title: "chore: update contributors"
delete-branch: true
body: |
This PR updates the contributors list in the static-data directory.
- name: Install GitHub CLI
run: sudo apt-get install -y gh
- name: Enable auto-merge
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
gh pr merge ${{steps.create-pull-request.outputs.pull-request-number}} --auto --squash
@@ -1,62 +0,0 @@
name: Update integration list
on:
workflow_dispatch: { }
push:
paths:
- packages/definitions/src/integration.ts
branches:
- dev
permissions:
pull-requests: write
jobs:
update-readme:
concurrency:
group: update-integration
cancel-in-progress: false
runs-on: ubuntu-latest
steps:
- name: Obtain token
id: obtainToken
uses: tibdex/github-app-token@v2
with:
private_key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
app_id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
- name: Checkout code
uses: actions/checkout@v5
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
- name: Setup
uses: ./tooling/github/setup
- run: pnpm run scripts:update-readme-integrations
- name: Commit changes
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
git config --global user.email "210161987+homarr-update-contributors[bot]@users.noreply.github.com"
git config --global user.name "Homarr Update Contributors"
git add .
git commit -m "chore: update integrations list readme"
- name: Create Pull Request
id: create-pull-request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.obtainToken.outputs.token }}
branch: update-integrations-readme
base: dev
title: "chore: update integrations list readme"
delete-branch: true
body: |
This PR automatically updates the list of integrations of Homarr in the README.md
- name: Install GitHub CLI
run: sudo apt-get install -y gh
- name: Enable auto-merge
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
gh pr merge ${{steps.create-pull-request.outputs.pull-request-number}} --auto --squash
+1 -1
View File
@@ -1 +1 @@
22.18.0
22.14.0
-1
View File
@@ -26,7 +26,6 @@
"Sabnzbd",
"SeDemal",
"Sonarr",
"sslverify",
"superjson",
"tabler",
"trpc",
-779
View File
@@ -1,782 +1,3 @@
## [1.34.0](https://github.com/homarr-labs/homarr/compare/v1.33.0...v1.34.0) (2025-08-22)
### Features
* **app-widget:** add layout option ([#3875](https://github.com/homarr-labs/homarr/issues/3875)) ([acd09c8](https://github.com/homarr-labs/homarr/commit/acd09c8c02c13daac6e5b2432bb72640f075cec6))
* **app-widget:** multiline description ([#3873](https://github.com/homarr-labs/homarr/issues/3873)) ([950636e](https://github.com/homarr-labs/homarr/commit/950636e81d4a934183f27694829dd3af05e5373f))
### Bug Fixes
* **deps:** update dependency drizzle-zod to ^0.8.3 ([#3866](https://github.com/homarr-labs/homarr/issues/3866)) ([dc43a01](https://github.com/homarr-labs/homarr/commit/dc43a015c43d13ae01a0f32be654c756f41eb352))
* **deps:** update dependency mantine-form-zod-resolver to ^1.3.0 ([#3868](https://github.com/homarr-labs/homarr/issues/3868)) ([72cdba7](https://github.com/homarr-labs/homarr/commit/72cdba79cbbaa03f35c90dd71a3703bacbe57558))
* **deps:** update dependency trpc-to-openapi to ^3.0.1 ([#3761](https://github.com/homarr-labs/homarr/issues/3761)) ([2457f9e](https://github.com/homarr-labs/homarr/commit/2457f9e4a754221e8674a95d721ffc77573a4633))
* **deps:** update dependency typescript-eslint to ^8.40.0 ([#3884](https://github.com/homarr-labs/homarr/issues/3884)) ([1a8e40e](https://github.com/homarr-labs/homarr/commit/1a8e40e1f9dbd4dbcf4ec0c54aef3297bc4cea7f))
* **deps:** update dependency undici to v7.14.0 ([#3879](https://github.com/homarr-labs/homarr/issues/3879)) ([d7b2298](https://github.com/homarr-labs/homarr/commit/d7b2298d1488ef8d38558864cf359fc23bc5bb26))
* **deps:** update dependency zod to ^4.0.17 ([#3867](https://github.com/homarr-labs/homarr/issues/3867)) ([82fcdae](https://github.com/homarr-labs/homarr/commit/82fcdae7e5cbc326f0051beb56c3a3805623e2a0))
* **deps:** update dependency zod-validation-error to v4 ([#3670](https://github.com/homarr-labs/homarr/issues/3670)) ([09fdb16](https://github.com/homarr-labs/homarr/commit/09fdb167a76db2caf3f9aa253b516ee28baba3dd))
* **deps:** update mantine monorepo to ^8.2.5 ([#3874](https://github.com/homarr-labs/homarr/issues/3874)) ([7e700b2](https://github.com/homarr-labs/homarr/commit/7e700b2732e2f0abff13d79191addb04097ca9bc))
* **deps:** update nextjs monorepo to v15.4.7 ([#3886](https://github.com/homarr-labs/homarr/issues/3886)) ([683e817](https://github.com/homarr-labs/homarr/commit/683e817493eb1b391751fe36b59d9b95a760324c))
* **deps:** update nextjs monorepo to v15.5.0 ([#3896](https://github.com/homarr-labs/homarr/issues/3896)) ([1451c3c](https://github.com/homarr-labs/homarr/commit/1451c3c41e08945eb31d025d187ff466c5104a05))
* **deps:** update tanstack-query monorepo to ^5.85.4 ([#3889](https://github.com/homarr-labs/homarr/issues/3889)) ([7685438](https://github.com/homarr-labs/homarr/commit/76854387903b48b3af7be466972cf7b9c165acc5))
* **deps:** update tanstack-query monorepo to ^5.85.5 ([#3890](https://github.com/homarr-labs/homarr/issues/3890)) ([fad7207](https://github.com/homarr-labs/homarr/commit/fad7207efe553111334b21739717b9813dea0b56))
* **nextcloud:** integration ignores trusted certificates ([#3893](https://github.com/homarr-labs/homarr/issues/3893)) ([498acdc](https://github.com/homarr-labs/homarr/commit/498acdcdc190de457dc95e5b13f43e1b2c31f624))
## [1.33.0](https://github.com/homarr-labs/homarr/compare/v1.32.0...v1.33.0) (2025-08-15)
### Features
* **tasks:** replace card layout with table interface for better UX ([#3804](https://github.com/homarr-labs/homarr/issues/3804)) ([9d14fcb](https://github.com/homarr-labs/homarr/commit/9d14fcba3646485d73d20455552e56602a346736))
### Bug Fixes
* **dashdot:** disabled widgets cause parse errors ([#3861](https://github.com/homarr-labs/homarr/issues/3861)) ([b7455d1](https://github.com/homarr-labs/homarr/commit/b7455d18edb3f4a76e9aec35f715cc722b13f0c3))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.6.2 ([#3854](https://github.com/homarr-labs/homarr/issues/3854)) ([8a4198d](https://github.com/homarr-labs/homarr/commit/8a4198db7e7fcb1e8556cf92c51888779ac6b614))
* **deps:** update dependency fastify to ^5.5.0 ([#3836](https://github.com/homarr-labs/homarr/issues/3836)) ([ff1e02e](https://github.com/homarr-labs/homarr/commit/ff1e02ec78b31a9cd155828b2f143fbae8e15b88))
* **deps:** update dependency jotai to ^2.13.1 ([#3834](https://github.com/homarr-labs/homarr/issues/3834)) ([5fe5d6f](https://github.com/homarr-labs/homarr/commit/5fe5d6f64d5c5b4096db8d4b2c7aefeab1fcef31))
* **deps:** update dependency tsx to v4.20.4 ([#3845](https://github.com/homarr-labs/homarr/issues/3845)) ([7a4961e](https://github.com/homarr-labs/homarr/commit/7a4961e9ad3e58ab09160f5cb7f0d8968f16d73a))
* **deps:** update dependency typescript-eslint to ^8.39.1 ([#3840](https://github.com/homarr-labs/homarr/issues/3840)) ([433be5e](https://github.com/homarr-labs/homarr/commit/433be5e82cbfb557bc1f69f119505c58f2612d25))
* **deps:** update tanstack-query monorepo to ^5.85.0 ([#3846](https://github.com/homarr-labs/homarr/issues/3846)) ([7d123b8](https://github.com/homarr-labs/homarr/commit/7d123b83e05a43d86d3822155cd9ed9d5cd4b55d))
* **deps:** update tanstack-query monorepo to ^5.85.1 ([#3858](https://github.com/homarr-labs/homarr/issues/3858)) ([70ce3e1](https://github.com/homarr-labs/homarr/commit/70ce3e168b0220486f90ef434ade61047de2ccb6))
* **deps:** update tanstack-query monorepo to ^5.85.2 ([#3859](https://github.com/homarr-labs/homarr/issues/3859)) ([e9cb878](https://github.com/homarr-labs/homarr/commit/e9cb878169b151c6090e2b736f6597be934bf4b5))
* **deps:** update tanstack-query monorepo to ^5.85.3 ([#3860](https://github.com/homarr-labs/homarr/issues/3860)) ([c72c154](https://github.com/homarr-labs/homarr/commit/c72c154305bc40fbaae728f3bffeeee065ab819d))
* **deps:** update turbo monorepo to ^2.5.6 ([#3865](https://github.com/homarr-labs/homarr/issues/3865)) ([98d344c](https://github.com/homarr-labs/homarr/commit/98d344c4b7f88fd0b7c6ce508b30cefd47f98ce5))
* **deps:** upgrade zod to v4 and fix breaking changes ([#3461](https://github.com/homarr-labs/homarr/issues/3461)) ([5c99622](https://github.com/homarr-labs/homarr/commit/5c99622fa86b7b0721384f089862a8405a27a61e))
* **mysql:** increase max media size restricted from 16kb to 4gb ([#3839](https://github.com/homarr-labs/homarr/issues/3839)) ([7ffcd1a](https://github.com/homarr-labs/homarr/commit/7ffcd1a08ede840cf67da2c74a4ba0965645a673))
## [1.32.0](https://github.com/homarr-labs/homarr/compare/v1.31.0...v1.32.0) (2025-08-08)
### Features
* **api:** add info endpoint ([#3737](https://github.com/homarr-labs/homarr/issues/3737)) ([baa652f](https://github.com/homarr-labs/homarr/commit/baa652f31493aad1490cf8e5d50bb04dc0006da1))
* system resources widget ([#3538](https://github.com/homarr-labs/homarr/issues/3538)) ([3ee408b](https://github.com/homarr-labs/homarr/commit/3ee408bf53c45f0b2abf04449b3653ed3e9cf225))
### Bug Fixes
* **deps:** update dependency @gitbeaker/rest to ^43.4.0 ([#3806](https://github.com/homarr-labs/homarr/issues/3806)) ([c73ce1f](https://github.com/homarr-labs/homarr/commit/c73ce1f704aa1fcbc6beac4cfa80b080b6800373))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.6.0 ([#3797](https://github.com/homarr-labs/homarr/issues/3797)) ([a4914d0](https://github.com/homarr-labs/homarr/commit/a4914d09fd6c9fa6a602f9f76b71c096369a34cd))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.6.1 ([#3809](https://github.com/homarr-labs/homarr/issues/3809)) ([ef40e75](https://github.com/homarr-labs/homarr/commit/ef40e7581896df4371c1f13487f3830bb57f6886))
* **deps:** update dependency jotai to ^2.13.0 ([#3763](https://github.com/homarr-labs/homarr/issues/3763)) ([c8d59d8](https://github.com/homarr-labs/homarr/commit/c8d59d88e5872783e767156eb063f329901fff6f))
* **deps:** update dependency sass to ^1.90.0 ([#3794](https://github.com/homarr-labs/homarr/issues/3794)) ([bb681f7](https://github.com/homarr-labs/homarr/commit/bb681f7b5ebb4a0858dac40eacfcc809c04b3d5e))
* **deps:** update dependency trpc-to-openapi to ^2.4.0 ([#3734](https://github.com/homarr-labs/homarr/issues/3734)) ([e45bef3](https://github.com/homarr-labs/homarr/commit/e45bef317a6ffae05d7a1685b1e6164e2ed09921))
* **deps:** update dependency typescript-eslint to ^8.39.0 ([#3781](https://github.com/homarr-labs/homarr/issues/3781)) ([1b5ccb5](https://github.com/homarr-labs/homarr/commit/1b5ccb529358836682a1cccd4a3da14f612884b9))
* **deps:** update dependency video.js to ^8.23.4 ([#3808](https://github.com/homarr-labs/homarr/issues/3808)) ([60accd6](https://github.com/homarr-labs/homarr/commit/60accd66f635ddb2f8c7a73faa865ee47aa40721))
* **deps:** update mantine monorepo to ^8.2.3 ([#3789](https://github.com/homarr-labs/homarr/issues/3789)) ([e6be8e1](https://github.com/homarr-labs/homarr/commit/e6be8e1db3d843e3d0e5b6d54c64999fd45f103d))
* **deps:** update mantine monorepo to ^8.2.4 ([#3803](https://github.com/homarr-labs/homarr/issues/3803)) ([ffa6290](https://github.com/homarr-labs/homarr/commit/ffa629064cdf202849d6f9c0ce645c6077c63e49))
* **deps:** update nextjs monorepo to v15.4.6 ([#3799](https://github.com/homarr-labs/homarr/issues/3799)) ([d327039](https://github.com/homarr-labs/homarr/commit/d32703910e0228a70a55a84ab712d6bd58fe7149))
* **deps:** update tanstack-query monorepo to ^5.84.2 ([#3810](https://github.com/homarr-labs/homarr/issues/3810)) ([d23196b](https://github.com/homarr-labs/homarr/commit/d23196b43034349d92cb9277633dd06b15ee0b7d))
* **deps:** update testcontainers-node monorepo to ^11.5.0 ([#3738](https://github.com/homarr-labs/homarr/issues/3738)) ([4bc053c](https://github.com/homarr-labs/homarr/commit/4bc053cc17d5a38db4255f9913e389fe86831676))
* **deps:** update testcontainers-node monorepo to ^11.5.1 ([#3796](https://github.com/homarr-labs/homarr/issues/3796)) ([ed950e9](https://github.com/homarr-labs/homarr/commit/ed950e9302450f33150d9800bc66e712bf8f6724))
* **deps:** update trpc monorepo to ^11.4.4 ([#3777](https://github.com/homarr-labs/homarr/issues/3777)) ([cc6d421](https://github.com/homarr-labs/homarr/commit/cc6d421e7db52b994e9e62fa1f828c72b5a20d72))
* **groups:** saving permissions with none activated causes error ([#3739](https://github.com/homarr-labs/homarr/issues/3739)) ([59c092f](https://github.com/homarr-labs/homarr/commit/59c092f8000f9a4e9fd70bde4176697dabb2e1c9))
* not hardcode hero banner scroll ([#3741](https://github.com/homarr-labs/homarr/issues/3741)) ([001f7a2](https://github.com/homarr-labs/homarr/commit/001f7a2a165fade9deaafb4cb319aa27926685e5))
* **opnsense:** switch secrets to api-key key and secret for easier understanding ([#3744](https://github.com/homarr-labs/homarr/issues/3744)) ([7f47468](https://github.com/homarr-labs/homarr/commit/7f474683a5e5b9ada978c055e57cd2b4114e17e0))
* some integrations handle parse errors wrong ([#3746](https://github.com/homarr-labs/homarr/issues/3746)) ([72e976e](https://github.com/homarr-labs/homarr/commit/72e976ee5ea28e549b7674c71b9a284e905e455b))
## [1.31.0](https://github.com/homarr-labs/homarr/compare/v1.30.1...v1.31.0) (2025-08-01)
### Features
* OPNsense integration and widget ([#3424](https://github.com/homarr-labs/homarr/issues/3424)) ([1dc1854](https://github.com/homarr-labs/homarr/commit/1dc1854cbfa39f4f22755cb84e1d6882e7e73721))
* **releases-widget:** add `Mark as read` action to mark releases as seen ([#3676](https://github.com/homarr-labs/homarr/issues/3676)) ([f63e646](https://github.com/homarr-labs/homarr/commit/f63e64627c79004b1e1bbb5a525fdde58f803da7))
* **releases-widget:** add `Select/Deselect All` to `import from docker` screen ([#3674](https://github.com/homarr-labs/homarr/issues/3674)) ([949c7a4](https://github.com/homarr-labs/homarr/commit/949c7a40d934b2d58a24bf9842f44703e73474ee))
* **releases-widget:** add new providers, Github Packages, linuxserver.io and Quay ([#3607](https://github.com/homarr-labs/homarr/issues/3607)) ([c92bbd2](https://github.com/homarr-labs/homarr/commit/c92bbd2da062221c3b5464838ad715c31a9d07b0))
* **releases-widget:** Allow to display only the icon ([#3695](https://github.com/homarr-labs/homarr/issues/3695)) ([03adf53](https://github.com/homarr-labs/homarr/commit/03adf538b03270404956dc16b191069f913e54d1))
### Bug Fixes
* characters are not considered Symbols when entering a new password ([#3689](https://github.com/homarr-labs/homarr/issues/3689)) ([6a819e3](https://github.com/homarr-labs/homarr/commit/6a819e38edca9f95b926a0162abdb6b873ddec08))
* **deps:** update dependency drizzle-orm to ^0.44.4 ([#3708](https://github.com/homarr-labs/homarr/issues/3708)) ([6126fac](https://github.com/homarr-labs/homarr/commit/6126fac915197c19ea6d448da8c4fc1b06b1383b))
* **deps:** update dependency ioredis to v5.7.0 ([#3718](https://github.com/homarr-labs/homarr/issues/3718)) ([4f9ecae](https://github.com/homarr-labs/homarr/commit/4f9ecae10406be4f6176947b517706843bd1c06d))
* **deps:** update dependency ldapts to v8.0.9 ([#3703](https://github.com/homarr-labs/homarr/issues/3703)) ([1aecf4f](https://github.com/homarr-labs/homarr/commit/1aecf4f90d4776bfa93b6f601fca16a7da8ffaba))
* **deps:** update dependency mysql2 to v3.14.3 ([#3711](https://github.com/homarr-labs/homarr/issues/3711)) ([952d4f7](https://github.com/homarr-labs/homarr/commit/952d4f71c9197cd88fa44ce5dbcf7d89f4c482d3))
* **deps:** update dependency swagger-ui-react to ^5.27.1 ([#3725](https://github.com/homarr-labs/homarr/issues/3725)) ([ab5e08a](https://github.com/homarr-labs/homarr/commit/ab5e08a4b09845e3aa4818214bc0f9ce830a2942))
* **deps:** update dependency undici to v7.13.0 ([#3717](https://github.com/homarr-labs/homarr/issues/3717)) ([b0bbd0d](https://github.com/homarr-labs/homarr/commit/b0bbd0d87ad6df7a9be83296060d8b0be6c091e0))
* **deps:** update mantine monorepo to ^8.2.2 ([#3715](https://github.com/homarr-labs/homarr/issues/3715)) ([1835641](https://github.com/homarr-labs/homarr/commit/183564176c9421faea70dff199d0b6f81325bf70))
* **deps:** update nextjs monorepo to v15.4.5 ([#3712](https://github.com/homarr-labs/homarr/issues/3712)) ([d0a112c](https://github.com/homarr-labs/homarr/commit/d0a112c7919cbcff9aaf2cc6b2dec8fd562246db))
* **deps:** update react monorepo to v19.1.1 ([#3706](https://github.com/homarr-labs/homarr/issues/3706)) ([0dc1f7c](https://github.com/homarr-labs/homarr/commit/0dc1f7cf14c0ca49d5eb8d37060773c3dab4834a))
* **deps:** update tanstack-query monorepo to ^5.83.1 ([#3719](https://github.com/homarr-labs/homarr/issues/3719)) ([f4e3713](https://github.com/homarr-labs/homarr/commit/f4e37134d806dac719012cb4dee19fb7e5daf7f2))
* **deps:** update tanstack-query monorepo to ^5.84.0 ([#3723](https://github.com/homarr-labs/homarr/issues/3723)) ([e7b2169](https://github.com/homarr-labs/homarr/commit/e7b21697efb0f90c3539152beaea94416cbb6fef))
* **deps:** update tanstack-query monorepo to ^5.84.1 ([#3729](https://github.com/homarr-labs/homarr/issues/3729)) ([511551a](https://github.com/homarr-labs/homarr/commit/511551aee7d5be9dadb70c596c5addbb646651cb))
### Reverts
* "feat(ping): ignore certificate error and show request durati… ([#3680](https://github.com/homarr-labs/homarr/issues/3680)) ([8eb44c9](https://github.com/homarr-labs/homarr/commit/8eb44c9f23584078b22605dac569387713dcd173))
## [1.30.1](https://github.com/homarr-labs/homarr/compare/v1.30.0...v1.30.1) (2025-07-25)
### Bug Fixes
* [#3667](https://github.com/homarr-labs/homarr/issues/3667) plex media releases ([#3682](https://github.com/homarr-labs/homarr/issues/3682)) ([588c134](https://github.com/homarr-labs/homarr/commit/588c134d913f6811f7ed39d1954b586acc294932))
* [#3678](https://github.com/homarr-labs/homarr/issues/3678) season number badge calendar ([#3681](https://github.com/homarr-labs/homarr/issues/3681)) ([470448e](https://github.com/homarr-labs/homarr/commit/470448e5fc1243dc78539dd8da87a974f504252a))
* **deps:** update dependency @ctrl/deluge to ^7.1.1 ([#3677](https://github.com/homarr-labs/homarr/issues/3677)) ([04c15de](https://github.com/homarr-labs/homarr/commit/04c15de0c87be963f333ccb6aa1932c445793672))
* **deps:** update dependency dotenv to ^17.2.1 ([#3684](https://github.com/homarr-labs/homarr/issues/3684)) ([d3fba3e](https://github.com/homarr-labs/homarr/commit/d3fba3eff4fd1fff3e86090436ecfd2fbcd93c28))
* **deps:** update dependency ldapts to v8.0.8 ([#3665](https://github.com/homarr-labs/homarr/issues/3665)) ([5a28535](https://github.com/homarr-labs/homarr/commit/5a28535f59f6ea1928a941d5c11050752dd3ecc9))
* **deps:** update dependency typescript-eslint to ^8.38.0 ([#3668](https://github.com/homarr-labs/homarr/issues/3668)) ([50a7d24](https://github.com/homarr-labs/homarr/commit/50a7d24b1b3a7cbfa6195525b10b994709b0beb3))
* **deps:** update mantine monorepo to ^8.2.1 ([#3666](https://github.com/homarr-labs/homarr/issues/3666)) ([9cdffc7](https://github.com/homarr-labs/homarr/commit/9cdffc7ead2668ed259c1a38c774a3efea5a1f0e))
* **deps:** update nextjs monorepo to v15.4.3 ([#3672](https://github.com/homarr-labs/homarr/issues/3672)) ([0094ec2](https://github.com/homarr-labs/homarr/commit/0094ec25677ca97f77920018c18f18a4b84812f8))
* **deps:** update nextjs monorepo to v15.4.4 ([#3683](https://github.com/homarr-labs/homarr/issues/3683)) ([abb011f](https://github.com/homarr-labs/homarr/commit/abb011ff9a7b930dad2c382c4e454fba29633364))
* **deps:** update testcontainers-node monorepo to ^11.3.2 ([#3671](https://github.com/homarr-labs/homarr/issues/3671)) ([d218170](https://github.com/homarr-labs/homarr/commit/d218170ec28a70b80c521efd1482164087d46e05))
* **deps:** update testcontainers-node monorepo to ^11.4.0 ([#3679](https://github.com/homarr-labs/homarr/issues/3679)) ([e858bad](https://github.com/homarr-labs/homarr/commit/e858bad2eda8847269c1beee4cfbf957fa318fe4))
## [1.30.0](https://github.com/homarr-labs/homarr/compare/v1.29.0...v1.30.0) (2025-07-21)
### Features
* **infra:** add external redis ([#3639](https://github.com/homarr-labs/homarr/issues/3639)) ([8e96032](https://github.com/homarr-labs/homarr/commit/8e960324bcc43a64100fd9c6c1fd6822c7759160))
* **widgets:** add media release widget ([#3219](https://github.com/homarr-labs/homarr/issues/3219)) ([66ebb50](https://github.com/homarr-labs/homarr/commit/66ebb5061fd219f9c804ef3a6bbe3372d296f740))
### Bug Fixes
* **deps:** update dependency @gitbeaker/rest to v43 ([#3623](https://github.com/homarr-labs/homarr/issues/3623)) ([732bce7](https://github.com/homarr-labs/homarr/commit/732bce72aee6c99d0ee94d04e173d378c2552a16))
* **deps:** update dependency @tabler/icons-react to ^3.34.1 ([#3661](https://github.com/homarr-labs/homarr/issues/3661)) ([bf88f0d](https://github.com/homarr-labs/homarr/commit/bf88f0d0918b4073a5770ab3589e00e5f89cacf1))
* **deps:** update nextjs monorepo to v15.4.2 ([#3654](https://github.com/homarr-labs/homarr/issues/3654)) ([42301b4](https://github.com/homarr-labs/homarr/commit/42301b4a1ed9e9f638b449a460862db9cd7ed50b))
* **deps:** update testcontainers-node monorepo to ^11.3.0 ([#3652](https://github.com/homarr-labs/homarr/issues/3652)) ([2394159](https://github.com/homarr-labs/homarr/commit/2394159f8b546e8b6e2d3cdc7c6bd0f75483209c))
* **deps:** update testcontainers-node monorepo to ^11.3.1 ([#3659](https://github.com/homarr-labs/homarr/issues/3659)) ([98f8279](https://github.com/homarr-labs/homarr/commit/98f8279b61013c312d492522e317bcf93f10e2a6))
* improved import from docker parsing ([#3642](https://github.com/homarr-labs/homarr/issues/3642)) ([36b0f57](https://github.com/homarr-labs/homarr/commit/36b0f576e5a00726d76695fd3300710be6f925f8))
* **security:** upgrade eslint-config-prettier to latest version without malicious code ([ecda928](https://github.com/homarr-labs/homarr/commit/ecda928bef407c67f01ce798c5f7a086cbd0dd9c))
## [1.29.0](https://github.com/homarr-labs/homarr/compare/v1.28.1...v1.29.0) (2025-07-18)
### Features
* **logs:** add log level selection to tools ui ([#3565](https://github.com/homarr-labs/homarr/issues/3565)) ([c4e5c30](https://github.com/homarr-labs/homarr/commit/c4e5c3002bd1c746cee496b5ba6d19159104b7f8))
* **releases-widget:** show project description when release description is missing ([#3635](https://github.com/homarr-labs/homarr/issues/3635)) ([47806c1](https://github.com/homarr-labs/homarr/commit/47806c180ebc960eb1731c02ec6af42156831bd0))
### Bug Fixes
* **deps:** update dependency drizzle-orm to ^0.44.3 ([#3629](https://github.com/homarr-labs/homarr/issues/3629)) ([e7012b6](https://github.com/homarr-labs/homarr/commit/e7012b6da496a0f4b7aad527d1d05cd03d0bfcda))
* **deps:** update dependency eslint-config-prettier to ^10.1.7 ([#3651](https://github.com/homarr-labs/homarr/issues/3651)) ([1cc6ecb](https://github.com/homarr-labs/homarr/commit/1cc6ecbce29348cf891dd8cfc29d4238ff5f7f78))
* **deps:** update dependency ldapts to v8.0.6 ([#3628](https://github.com/homarr-labs/homarr/issues/3628)) ([71dee5c](https://github.com/homarr-labs/homarr/commit/71dee5ca4bca04acc9c295c373d1209641ef3d99))
* **deps:** update dependency swagger-ui-react to ^5.27.0 ([#3638](https://github.com/homarr-labs/homarr/issues/3638)) ([ee983e8](https://github.com/homarr-labs/homarr/commit/ee983e8241d2f32c1dcbbea4930b18442f32a5bf))
* **deps:** update dependency typescript-eslint to ^8.37.0 ([#3631](https://github.com/homarr-labs/homarr/issues/3631)) ([d6ea8e8](https://github.com/homarr-labs/homarr/commit/d6ea8e8993d130a03592473744e74be7b4c54a79))
* **deps:** update dependency undici to v7.12.0 ([#3650](https://github.com/homarr-labs/homarr/issues/3650)) ([cf0d2d3](https://github.com/homarr-labs/homarr/commit/cf0d2d3cdaf495d7786c45ba4c79d49f356c0e37))
* **deps:** update nextjs monorepo to v15.4.1 ([#3632](https://github.com/homarr-labs/homarr/issues/3632)) ([39b025f](https://github.com/homarr-labs/homarr/commit/39b025f7633391c74aedeb4c4dca10d42fb3b4d8))
* **deps:** update turbo monorepo to ^2.5.5 ([#3641](https://github.com/homarr-labs/homarr/issues/3641)) ([d4acb01](https://github.com/homarr-labs/homarr/commit/d4acb01efd6a9341fb5550a08def42d82c1f0b92))
## [1.28.1](https://github.com/homarr-labs/homarr/compare/v1.28.0...v1.28.1) (2025-07-12)
### Bug Fixes
* **deps:** update tiptap monorepo to v2.26.1 ([#3603](https://github.com/homarr-labs/homarr/issues/3603)) ([473a9f4](https://github.com/homarr-labs/homarr/commit/473a9f47f352a532128c1bf2c02490e8e4c6ca7c))
* **releases-widget:** fixed bug with changing a missing provider ([#3605](https://github.com/homarr-labs/homarr/issues/3605)) ([5ecc4db](https://github.com/homarr-labs/homarr/commit/5ecc4db5dabcfd4f953f3b436527d34ea9191eef))
## [1.28.0](https://github.com/homarr-labs/homarr/compare/v1.27.0...v1.28.0) (2025-07-11)
### Features
* **icon-color:** add icon color clear button ([#3593](https://github.com/homarr-labs/homarr/issues/3593)) ([5a8653d](https://github.com/homarr-labs/homarr/commit/5a8653d8500388d9392b150d62a56474c05843d7))
* **notebook:** add placeholder, redo and undo button ([#3564](https://github.com/homarr-labs/homarr/issues/3564)) ([3411354](https://github.com/homarr-labs/homarr/commit/3411354677fedec6ff5b5b85eb25eacc94308425))
* **ping:** ignore certificate error and show request duration ([#3546](https://github.com/homarr-labs/homarr/issues/3546)) ([1eb4731](https://github.com/homarr-labs/homarr/commit/1eb47311fa81184bd67830d0669d9f87d9922e70))
* **releases-widget:** define providers as integrations ([#3253](https://github.com/homarr-labs/homarr/issues/3253)) ([5d8126d](https://github.com/homarr-labs/homarr/commit/5d8126d71eea61af9c023895cb911d24544f8066))
### Bug Fixes
* **bookmarks:** wrong widget icon ([#3572](https://github.com/homarr-labs/homarr/issues/3572)) ([fb57d46](https://github.com/homarr-labs/homarr/commit/fb57d46b573cc9798469540f4823cbf73afd21a5))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.5.0 ([#3585](https://github.com/homarr-labs/homarr/issues/3585)) ([ee286c9](https://github.com/homarr-labs/homarr/commit/ee286c95563c99297b5419c005550936ca96757b))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.5.1 ([#3586](https://github.com/homarr-labs/homarr/issues/3586)) ([7e92f1b](https://github.com/homarr-labs/homarr/commit/7e92f1bafadf1188ee3ddc9d2dc9904f103aab4e))
* **deps:** update dependency dotenv to ^17.1.0 ([#3567](https://github.com/homarr-labs/homarr/issues/3567)) ([a369beb](https://github.com/homarr-labs/homarr/commit/a369beb248e2cac1f1dcf32421efb48f241700a6))
* **deps:** update dependency dotenv to ^17.2.0 ([#3578](https://github.com/homarr-labs/homarr/issues/3578)) ([1724f9d](https://github.com/homarr-labs/homarr/commit/1724f9d26c5761e3195d336eb451dd47855266a4))
* **deps:** update dependency ldapts to v8.0.5 ([#3559](https://github.com/homarr-labs/homarr/issues/3559)) ([8b9832b](https://github.com/homarr-labs/homarr/commit/8b9832b2b81dcc1a4f9081755defe44fc0f38a1d))
* **deps:** update dependency mysql2 to v3.14.2 ([#3592](https://github.com/homarr-labs/homarr/issues/3592)) ([d68b80e](https://github.com/homarr-labs/homarr/commit/d68b80e6be38366860de84778a45e5a43aae06eb))
* **deps:** update dependency node-cron to ^4.2.1 ([#3590](https://github.com/homarr-labs/homarr/issues/3590)) ([e21b236](https://github.com/homarr-labs/homarr/commit/e21b236d37f076654768e88bc7ef139a3e51abbc))
* **deps:** update dependency swagger-ui-react to ^5.26.1 ([#3554](https://github.com/homarr-labs/homarr/issues/3554)) ([12f4e90](https://github.com/homarr-labs/homarr/commit/12f4e90e440a8ea1708a2a9ef64a851ef977bd6c))
* **deps:** update dependency swagger-ui-react to ^5.26.2 ([#3558](https://github.com/homarr-labs/homarr/issues/3558)) ([57be43f](https://github.com/homarr-labs/homarr/commit/57be43f51b7ef306b962299789194de68ec9e8ea))
* **deps:** update dependency typescript-eslint to ^8.36.0 ([#3563](https://github.com/homarr-labs/homarr/issues/3563)) ([7c0a742](https://github.com/homarr-labs/homarr/commit/7c0a7428d85d11f3d2226d809ba04671ca713b72))
* **deps:** update dependency zod to ^3.25.75 ([#3555](https://github.com/homarr-labs/homarr/issues/3555)) ([e9a35ba](https://github.com/homarr-labs/homarr/commit/e9a35ba811bd1807d63c5f0c760d470b20f4410f))
* **deps:** update dependency zod to ^3.25.76 ([#3569](https://github.com/homarr-labs/homarr/issues/3569)) ([c793bf1](https://github.com/homarr-labs/homarr/commit/c793bf1982c3a484176860cebad80c721acd597d))
* **deps:** update dependency zod-validation-error to ^3.5.3 ([#3600](https://github.com/homarr-labs/homarr/issues/3600)) ([9020440](https://github.com/homarr-labs/homarr/commit/9020440193595a5b437464957d9a3745fb200c93))
* **deps:** update mantine monorepo to ^8.1.3 ([#3560](https://github.com/homarr-labs/homarr/issues/3560)) ([1fe3450](https://github.com/homarr-labs/homarr/commit/1fe34505550f401be97c9d84c383ea70bf39b937))
* **deps:** update tanstack-query monorepo to ^5.82.0 ([#3577](https://github.com/homarr-labs/homarr/issues/3577)) ([90f6a2d](https://github.com/homarr-labs/homarr/commit/90f6a2dd014a73cd0131781b6db56a1056e563b6))
* **deps:** update tanstack-query monorepo to ^5.83.0 ([#3599](https://github.com/homarr-labs/homarr/issues/3599)) ([bd8ac62](https://github.com/homarr-labs/homarr/commit/bd8ac62c7af034525803410751bfd5ab72094f45))
* **deps:** update testcontainers-node monorepo to ^11.1.0 ([#3550](https://github.com/homarr-labs/homarr/issues/3550)) ([b0bdf28](https://github.com/homarr-labs/homarr/commit/b0bdf28016bae2ccfab24cab6df5b4846d1a8f48))
* **deps:** update testcontainers-node monorepo to ^11.2.0 ([#3556](https://github.com/homarr-labs/homarr/issues/3556)) ([824f443](https://github.com/homarr-labs/homarr/commit/824f443949e75ec3f3edacb9093faefbdec47ef4))
* **deps:** update testcontainers-node monorepo to ^11.2.1 ([#3562](https://github.com/homarr-labs/homarr/issues/3562)) ([5d394c8](https://github.com/homarr-labs/homarr/commit/5d394c849f5f47b47c595f73763e89d0a5d6247a))
* **deps:** update tiptap monorepo to v2.25.0 ([#3552](https://github.com/homarr-labs/homarr/issues/3552)) ([430dc58](https://github.com/homarr-labs/homarr/commit/430dc580682cf0885fcd0ef2b7b81a0dfdf93425))
* **deps:** update tiptap monorepo to v2.25.1 ([#3591](https://github.com/homarr-labs/homarr/issues/3591)) ([661afa7](https://github.com/homarr-labs/homarr/commit/661afa72be96c629cad4ab7f6007214b54d8ef6a))
* **deps:** update tiptap monorepo to v2.26.0 ([#3596](https://github.com/homarr-labs/homarr/issues/3596)) ([df5b6fc](https://github.com/homarr-labs/homarr/commit/df5b6fcb6a6215f5c1cf92e034e27f6e32405b3e))
* unable to change certificate path for running from source ([#3582](https://github.com/homarr-labs/homarr/issues/3582)) ([f1143e5](https://github.com/homarr-labs/homarr/commit/f1143e54a06e00a348e37b5633fad4cb6390cff7))
## [1.27.0](https://github.com/homarr-labs/homarr/compare/v1.26.0...v1.27.0) (2025-07-04)
### Features
* **integrations:** add mock integration ([#3505](https://github.com/homarr-labs/homarr/issues/3505)) ([58d5b14](https://github.com/homarr-labs/homarr/commit/58d5b14c516583e79f9079899dfe6544acdc58f7))
* **tasks:** allow management of job intervals and disabling them ([#3408](https://github.com/homarr-labs/homarr/issues/3408)) ([9398dd9](https://github.com/homarr-labs/homarr/commit/9398dd983cce00959af2a4885e92da6acfe2f57e))
### Bug Fixes
* broken lockfile ([308d235](https://github.com/homarr-labs/homarr/commit/308d235fa9db658fb7c3f5a183f01c4f48f0546f))
* **deps:** update dependency better-sqlite3 to ^12.2.0 ([#3503](https://github.com/homarr-labs/homarr/issues/3503)) ([c22a963](https://github.com/homarr-labs/homarr/commit/c22a963342c5613089b0a1a4034379cc80425602))
* **deps:** update dependency dotenv to ^16.6.1 ([#3494](https://github.com/homarr-labs/homarr/issues/3494)) ([b880de8](https://github.com/homarr-labs/homarr/commit/b880de8a794a879a0c2b38cc46cb5412eedbb44e))
* **deps:** update dependency dotenv to v17 ([#3499](https://github.com/homarr-labs/homarr/issues/3499)) ([fe87d97](https://github.com/homarr-labs/homarr/commit/fe87d978c4335a700e2859c648bf997a7c323a31))
* **deps:** update dependency ldapts to v8.0.4 ([#3509](https://github.com/homarr-labs/homarr/issues/3509)) ([8d53801](https://github.com/homarr-labs/homarr/commit/8d53801f67b5458b5eb60b4ebcd6a923a60b7e5f))
* **deps:** update dependency next-intl to v4.3.2 ([#3520](https://github.com/homarr-labs/homarr/issues/3520)) ([6fda939](https://github.com/homarr-labs/homarr/commit/6fda939a2d94120998eb1365134652eb00602937))
* **deps:** update dependency next-intl to v4.3.3 ([#3521](https://github.com/homarr-labs/homarr/issues/3521)) ([ab9165f](https://github.com/homarr-labs/homarr/commit/ab9165f2cd885069c7389d62db187c3be240fd72))
* **deps:** update dependency next-intl to v4.3.4 ([#3528](https://github.com/homarr-labs/homarr/issues/3528)) ([6005784](https://github.com/homarr-labs/homarr/commit/60057842842d393b8d9c5404d6c2ae85bc82b27c))
* **deps:** update dependency node-cron to ^4.2.0 ([#3518](https://github.com/homarr-labs/homarr/issues/3518)) ([0f94ac4](https://github.com/homarr-labs/homarr/commit/0f94ac456eeacbc03183d635ea4c57b224cf8537))
* **deps:** update dependency postcss-preset-mantine to ^1.18.0 ([#3510](https://github.com/homarr-labs/homarr/issues/3510)) ([01d0ffb](https://github.com/homarr-labs/homarr/commit/01d0ffb0d143a77c94c977873ad04f661c280eb7))
* **deps:** update dependency swagger-ui-react to ^5.25.4 ([#3523](https://github.com/homarr-labs/homarr/issues/3523)) ([6c94d1a](https://github.com/homarr-labs/homarr/commit/6c94d1a10c495850958b6399a302eab582e9d40b))
* **deps:** update dependency swagger-ui-react to ^5.26.0 ([#3527](https://github.com/homarr-labs/homarr/issues/3527)) ([9da66f5](https://github.com/homarr-labs/homarr/commit/9da66f5b1db88c196dbb6866f920f1a176305667))
* **deps:** update dependency trpc-to-openapi to ^2.3.2 ([#3502](https://github.com/homarr-labs/homarr/issues/3502)) ([2ebc9f9](https://github.com/homarr-labs/homarr/commit/2ebc9f969b5a0c753622421cb5f0a81873ad4ae9))
* **deps:** update dependency typescript-eslint to ^8.35.1 ([#3513](https://github.com/homarr-labs/homarr/issues/3513)) ([17fe293](https://github.com/homarr-labs/homarr/commit/17fe293a7c9e6dbc83bc8f585a54a8fdd8640973))
* **deps:** update dependency ws to ^8.18.3 ([#3501](https://github.com/homarr-labs/homarr/issues/3501)) ([ed077d6](https://github.com/homarr-labs/homarr/commit/ed077d63db9a197510e8e139d40813a3854f39e9))
* **deps:** update dependency zod to ^3.25.68 ([#3529](https://github.com/homarr-labs/homarr/issues/3529)) ([b375ec9](https://github.com/homarr-labs/homarr/commit/b375ec977e757bed14b6fb6a04a5b30fe481eaf9))
* **deps:** update dependency zod to ^3.25.69 ([#3531](https://github.com/homarr-labs/homarr/issues/3531)) ([915eec1](https://github.com/homarr-labs/homarr/commit/915eec1b5e6d7383d36f2e67d86472c6a2932a9b))
* **deps:** update dependency zod to ^3.25.71 ([#3534](https://github.com/homarr-labs/homarr/issues/3534)) ([95c8aad](https://github.com/homarr-labs/homarr/commit/95c8aadb0c98f116ff3e256c770636267da82fdc))
* **deps:** update dependency zod to ^3.25.72 ([#3535](https://github.com/homarr-labs/homarr/issues/3535)) ([0858a42](https://github.com/homarr-labs/homarr/commit/0858a42b8137f003fb7500f0dbccbb9413a6c470))
* **deps:** update dependency zod to ^3.25.73 ([#3543](https://github.com/homarr-labs/homarr/issues/3543)) ([7061a03](https://github.com/homarr-labs/homarr/commit/7061a03b2ef90c51754a4e39b22742c34e5ebc80))
* **deps:** update dependency zod to ^3.25.74 ([#3547](https://github.com/homarr-labs/homarr/issues/3547)) ([b057059](https://github.com/homarr-labs/homarr/commit/b057059ff1fb8fcd29893000d30ddcba09f2b775))
* **deps:** update nextjs monorepo to v15.3.5 ([#3541](https://github.com/homarr-labs/homarr/issues/3541)) ([d5181d0](https://github.com/homarr-labs/homarr/commit/d5181d0c0e1f25a119a9e91a9544a8f76e0b0901))
* **deps:** update tanstack-query monorepo to ^5.81.5 ([#3495](https://github.com/homarr-labs/homarr/issues/3495)) ([5346b96](https://github.com/homarr-labs/homarr/commit/5346b9698444a503e432ee4b0679e408e2c44d96))
* **deps:** update tiptap monorepo to v2.23.1 ([#3516](https://github.com/homarr-labs/homarr/issues/3516)) ([0a0e7f9](https://github.com/homarr-labs/homarr/commit/0a0e7f9602429003d45b99fade5f1ad121aa67f3))
* **deps:** update tiptap monorepo to v2.24.0 ([#3532](https://github.com/homarr-labs/homarr/issues/3532)) ([0e1f693](https://github.com/homarr-labs/homarr/commit/0e1f6934f9755503ce289b71549b6737171e1c57))
* **deps:** update tiptap monorepo to v2.24.1 ([#3536](https://github.com/homarr-labs/homarr/issues/3536)) ([63e2ace](https://github.com/homarr-labs/homarr/commit/63e2acefbdf09d4aa7e5a9522cfb4b0c89fb66e9))
* **deps:** update tiptap monorepo to v2.24.2 ([#3544](https://github.com/homarr-labs/homarr/issues/3544)) ([66f663a](https://github.com/homarr-labs/homarr/commit/66f663aad4b35d0cbfb1ffd60545fd0272b70990))
## [1.26.0](https://github.com/homarr-labs/homarr/compare/v1.25.0...v1.26.0) (2025-06-27)
### Features
* add ntfy integration ([#2900](https://github.com/homarr-labs/homarr/issues/2900)) ([e110a84](https://github.com/homarr-labs/homarr/commit/e110a84fdd5fa2468786e1505043d8a7dc5cfa1b))
### Bug Fixes
* **deps:** update dependency @auth/core to ^0.40.0 ([#3448](https://github.com/homarr-labs/homarr/issues/3448)) ([2c7f5de](https://github.com/homarr-labs/homarr/commit/2c7f5de19271f7c65a9979b37d9602bb84c68564))
* **deps:** update dependency @auth/drizzle-adapter to ^1.10.0 ([#3449](https://github.com/homarr-labs/homarr/issues/3449)) ([7b8dc16](https://github.com/homarr-labs/homarr/commit/7b8dc1670d0c8473524f71e1ee872e6ea7d2e0ce))
* **deps:** update dependency @tiptap/extension-highlight to v2.22.1 ([#3433](https://github.com/homarr-labs/homarr/issues/3433)) ([f6039a5](https://github.com/homarr-labs/homarr/commit/f6039a5976da70388cb32eecfbf57ac02e6fc050))
* **deps:** update dependency better-sqlite3 to ^12.1.0 ([#3462](https://github.com/homarr-labs/homarr/issues/3462)) ([fa421b3](https://github.com/homarr-labs/homarr/commit/fa421b3778c00b30b68ceadfb370803eba1a72f2))
* **deps:** update dependency better-sqlite3 to ^12.1.1 ([#3470](https://github.com/homarr-labs/homarr/issues/3470)) ([eadec21](https://github.com/homarr-labs/homarr/commit/eadec212369b8ee81eec00b3ecbd40d1c90972d7))
* **deps:** update dependency better-sqlite3 to v12 ([#3441](https://github.com/homarr-labs/homarr/issues/3441)) ([95be039](https://github.com/homarr-labs/homarr/commit/95be0391a651b59a2611ca5ffa7825c6b337a996))
* **deps:** update dependency dotenv to ^16.6.0 ([#3484](https://github.com/homarr-labs/homarr/issues/3484)) ([7477d02](https://github.com/homarr-labs/homarr/commit/7477d02cadd88608f33edf0836f7d7e164800dde))
* **deps:** update dependency drizzle-kit to ^0.31.2 ([#3455](https://github.com/homarr-labs/homarr/issues/3455)) ([8ffbbd6](https://github.com/homarr-labs/homarr/commit/8ffbbd6bf9bb1c329943ae5033029eed115b43dd))
* **deps:** update dependency drizzle-kit to ^0.31.3 ([#3482](https://github.com/homarr-labs/homarr/issues/3482)) ([7d0ac46](https://github.com/homarr-labs/homarr/commit/7d0ac46753b1cf6bee1f53297b0011e4e86fdffd))
* **deps:** update dependency drizzle-kit to ^0.31.4 ([#3490](https://github.com/homarr-labs/homarr/issues/3490)) ([670de0f](https://github.com/homarr-labs/homarr/commit/670de0f5fbb9faf950fdb38e4f4cfb57f21257c4))
* **deps:** update dependency eslint-plugin-import to ^2.32.0 ([#3430](https://github.com/homarr-labs/homarr/issues/3430)) ([a7052bb](https://github.com/homarr-labs/homarr/commit/a7052bb16f52ccb47d2c8ac205fdf01cf8e8c01e))
* **deps:** update dependency ldapts to v8.0.2 ([#3481](https://github.com/homarr-labs/homarr/issues/3481)) ([114de40](https://github.com/homarr-labs/homarr/commit/114de40634b501ecea7be8d9ae1d2a19686845c1))
* **deps:** update dependency maria2 to ^0.4.1 ([#3456](https://github.com/homarr-labs/homarr/issues/3456)) ([4e892ef](https://github.com/homarr-labs/homarr/commit/4e892efab7f456b93e993c2c3cb6068f06ea1e4f))
* **deps:** update dependency next-auth to v5.0.0-beta.29 ([#3447](https://github.com/homarr-labs/homarr/issues/3447)) ([da2d22b](https://github.com/homarr-labs/homarr/commit/da2d22b1b9b87fe7aa1cbac76909af2d2ef57d3f))
* **deps:** update dependency next-intl to v4.2.0 ([#3457](https://github.com/homarr-labs/homarr/issues/3457)) ([86d4585](https://github.com/homarr-labs/homarr/commit/86d4585035c5604a219fd0be404104f62a0be0b2))
* **deps:** update dependency next-intl to v4.3.0 ([#3458](https://github.com/homarr-labs/homarr/issues/3458)) ([ded3993](https://github.com/homarr-labs/homarr/commit/ded39930ca1d8c4868f9541c7fad18a234ceeb3f))
* **deps:** update dependency next-intl to v4.3.1 ([#3465](https://github.com/homarr-labs/homarr/issues/3465)) ([5d96744](https://github.com/homarr-labs/homarr/commit/5d9674412e6e99af1fb1fce7143ce4b6717ff29b))
* **deps:** update dependency prettier to ^3.6.0 ([#3452](https://github.com/homarr-labs/homarr/issues/3452)) ([07ce1c1](https://github.com/homarr-labs/homarr/commit/07ce1c1a2d197f981818a9b1e567102f0772271d))
* **deps:** update dependency prettier to ^3.6.1 ([#3469](https://github.com/homarr-labs/homarr/issues/3469)) ([34faa9e](https://github.com/homarr-labs/homarr/commit/34faa9e58ce061c4c99d207cfc6c56cbb7045c03))
* **deps:** update dependency prettier to ^3.6.2 ([#3489](https://github.com/homarr-labs/homarr/issues/3489)) ([60786bf](https://github.com/homarr-labs/homarr/commit/60786bf9ac81ce13b6304f5284fb6049e2d7d7dc))
* **deps:** update dependency recharts to ^2.15.4 ([#3429](https://github.com/homarr-labs/homarr/issues/3429)) ([caedac4](https://github.com/homarr-labs/homarr/commit/caedac490ed3aaf8f6a815d799ba9fafe9de18a2))
* **deps:** update dependency swagger-ui-react to ^5.25.3 ([#3479](https://github.com/homarr-labs/homarr/issues/3479)) ([1cd9fa8](https://github.com/homarr-labs/homarr/commit/1cd9fa8e82eef1f69be06855d076a364883914df))
* **deps:** update dependency typescript-eslint to ^8.35.0 ([#3460](https://github.com/homarr-labs/homarr/issues/3460)) ([1813d44](https://github.com/homarr-labs/homarr/commit/1813d4435e08f6a800ee76561e66e6371b30ec80))
* **deps:** update dependency undici to v7.11.0 ([#3486](https://github.com/homarr-labs/homarr/issues/3486)) ([01fdd79](https://github.com/homarr-labs/homarr/commit/01fdd7955906c661a620b8fac148693dc4632ebd))
* **deps:** update mantine monorepo to ^8.1.2 ([#3471](https://github.com/homarr-labs/homarr/issues/3471)) ([5e6dfb3](https://github.com/homarr-labs/homarr/commit/5e6dfb3a5c2d456fa20fbcd07f98c01d0ab7c4b7))
* **deps:** update tanstack-query monorepo to ^5.80.12 ([#3436](https://github.com/homarr-labs/homarr/issues/3436)) ([86608f5](https://github.com/homarr-labs/homarr/commit/86608f5a9ffe9ea2c57a9191175994a24eea5dc7))
* **deps:** update tanstack-query monorepo to ^5.81.0 ([#3437](https://github.com/homarr-labs/homarr/issues/3437)) ([e6f05f9](https://github.com/homarr-labs/homarr/commit/e6f05f9d94912ef5f38ad4ab222e20d433c627eb))
* **deps:** update tanstack-query monorepo to ^5.81.1 ([#3440](https://github.com/homarr-labs/homarr/issues/3440)) ([b8b71da](https://github.com/homarr-labs/homarr/commit/b8b71da291541b714e053ea1bda4e49053261936))
* **deps:** update tanstack-query monorepo to ^5.81.2 ([#3442](https://github.com/homarr-labs/homarr/issues/3442)) ([3f0e9c3](https://github.com/homarr-labs/homarr/commit/3f0e9c37385ae23370618f1a11870642a9b3d030))
* **deps:** update tanstack-query monorepo to ^5.81.4 ([#3493](https://github.com/homarr-labs/homarr/issues/3493)) ([a737331](https://github.com/homarr-labs/homarr/commit/a737331d207abfd4ab8a8869c70257b5f5d50d5a))
* **deps:** update tiptap monorepo to v2.22.1 ([#3432](https://github.com/homarr-labs/homarr/issues/3432)) ([8ccbe7f](https://github.com/homarr-labs/homarr/commit/8ccbe7ff52eff742031c528bcc1e15916ba1fe81))
* **deps:** update tiptap monorepo to v2.22.2 ([#3438](https://github.com/homarr-labs/homarr/issues/3438)) ([f677bd6](https://github.com/homarr-labs/homarr/commit/f677bd6b12630130d8cf95a4f2462e4bab1226db))
* **deps:** update tiptap monorepo to v2.22.3 ([#3443](https://github.com/homarr-labs/homarr/issues/3443)) ([add73a1](https://github.com/homarr-labs/homarr/commit/add73a1a3c19e0efd2d417463e4f4d72a9fd1c02))
* **deps:** update tiptap monorepo to v2.23.0 ([#3487](https://github.com/homarr-labs/homarr/issues/3487)) ([b027f3a](https://github.com/homarr-labs/homarr/commit/b027f3a18084743b452dd35ff8e0fa30f59acd85))
* **deps:** update trpc monorepo to ^11.4.3 ([#3491](https://github.com/homarr-labs/homarr/issues/3491)) ([d326f3b](https://github.com/homarr-labs/homarr/commit/d326f3b4b3f5fc342404a12b25380d9dd6cd924a))
* **jellyseerr:** add missing deleted media status ([#3439](https://github.com/homarr-labs/homarr/issues/3439)) ([6b474c3](https://github.com/homarr-labs/homarr/commit/6b474c3df470450598b6b9472bf9f52740b8ed21))
## [1.25.0](https://github.com/homarr-labs/homarr/compare/v1.24.0...v1.25.0) (2025-06-20)
### Features
* **integrations:** add support for self-signed-cert-in-chain request error ([#3399](https://github.com/homarr-labs/homarr/issues/3399)) ([e6cb35b](https://github.com/homarr-labs/homarr/commit/e6cb35b7c2dc45349a9cf0819aa21228aea03e26))
### Bug Fixes
* **deps:** update dependency node-cron to ^4.1.1 ([#3427](https://github.com/homarr-labs/homarr/issues/3427)) ([f6a99ff](https://github.com/homarr-labs/homarr/commit/f6a99ffedd690c01e18f445ade6343f1ca476b27))
* **deps:** update dependency swagger-ui-react to ^5.24.2 ([#3405](https://github.com/homarr-labs/homarr/issues/3405)) ([bdca920](https://github.com/homarr-labs/homarr/commit/bdca92036a3dde7f655735c305e55c5c15b63440))
* **deps:** update dependency swagger-ui-react to ^5.25.2 ([#3420](https://github.com/homarr-labs/homarr/issues/3420)) ([02cd7a5](https://github.com/homarr-labs/homarr/commit/02cd7a50dad9e954cfd569b0f1efcc4fe4fd5da4))
* **deps:** update dependency tsdav to ^2.1.5 ([#3414](https://github.com/homarr-labs/homarr/issues/3414)) ([6bcfe78](https://github.com/homarr-labs/homarr/commit/6bcfe789561c518e7ce6c78a9be95412a50f04dd))
* **deps:** update dependency typescript-eslint to ^8.34.1 ([#3409](https://github.com/homarr-labs/homarr/issues/3409)) ([637bfb8](https://github.com/homarr-labs/homarr/commit/637bfb8b47b7dc6c70163bd481c47bca9bd93d55))
* **deps:** update dependency zod to ^3.25.65 ([#3410](https://github.com/homarr-labs/homarr/issues/3410)) ([a8c9c07](https://github.com/homarr-labs/homarr/commit/a8c9c075f08bca4b86307e98156cae7167ef6ab1))
* **deps:** update dependency zod to ^3.25.67 ([#3413](https://github.com/homarr-labs/homarr/issues/3413)) ([56ca8b7](https://github.com/homarr-labs/homarr/commit/56ca8b7b0b1c0187457c085af1c7f4504d72ed9a))
* **deps:** update dependency zod-validation-error to ^3.5.1 ([#3406](https://github.com/homarr-labs/homarr/issues/3406)) ([adbef25](https://github.com/homarr-labs/homarr/commit/adbef252c61c29fbbc8c7b0e325a0e9d69bb3191))
* **deps:** update dependency zod-validation-error to ^3.5.2 ([#3417](https://github.com/homarr-labs/homarr/issues/3417)) ([430f671](https://github.com/homarr-labs/homarr/commit/430f6714345b5b90859fd3302f76fd3d14956148))
* **deps:** update mantine monorepo to ^8.1.1 ([#3404](https://github.com/homarr-labs/homarr/issues/3404)) ([f97a261](https://github.com/homarr-labs/homarr/commit/f97a2619734dcf3b326c69587fa2f4e170cb9c88))
* **deps:** update nextjs monorepo to v15.3.4 ([#3421](https://github.com/homarr-labs/homarr/issues/3421)) ([3759a98](https://github.com/homarr-labs/homarr/commit/3759a9831c6fe54b0b0eb0e101c8eb912b752b41))
* **deps:** update tanstack-query monorepo to ^5.80.10 ([#3422](https://github.com/homarr-labs/homarr/issues/3422)) ([7c58e8a](https://github.com/homarr-labs/homarr/commit/7c58e8ac9bdb2868637648b43b7965189421b295))
* **deps:** update tiptap monorepo to v2.14.1 ([#3423](https://github.com/homarr-labs/homarr/issues/3423)) ([9e4b463](https://github.com/homarr-labs/homarr/commit/9e4b4635bc582e4d92821844d58fdb3d057f7fec))
* **deps:** update tiptap monorepo to v2.22.0 ([#3425](https://github.com/homarr-labs/homarr/issues/3425)) ([ec4dde6](https://github.com/homarr-labs/homarr/commit/ec4dde64fdd4302b091a0255ec1bcdc6b2caca89))
* **deps:** update trpc monorepo to ^11.4.2 ([#3419](https://github.com/homarr-labs/homarr/issues/3419)) ([8e428e2](https://github.com/homarr-labs/homarr/commit/8e428e20a764c54c15fc44b57f85fa684cfbc00e))
* **downloads:** not updating automatically ([#3412](https://github.com/homarr-labs/homarr/issues/3412)) ([b5ef7e5](https://github.com/homarr-labs/homarr/commit/b5ef7e5194a828abfddcac5eb492910de5f1f7d8))
* **widget-integration-select:** fixed error about `<p>` being descendant of `<p>` ([#3418](https://github.com/homarr-labs/homarr/issues/3418)) ([73fff02](https://github.com/homarr-labs/homarr/commit/73fff02ad0a3a710aa39cda7dccf9206569c2879))
## [1.24.0](https://github.com/homarr-labs/homarr/compare/v1.23.0...v1.24.0) (2025-06-13)
### Features
* reduce cron job intervals ([#3235](https://github.com/homarr-labs/homarr/issues/3235)) ([f67702e](https://github.com/homarr-labs/homarr/commit/f67702e1d32954b4f6db28c02b1a25e56122d779))
### Bug Fixes
* csp prevents external videos ([#3372](https://github.com/homarr-labs/homarr/issues/3372)) ([c59cded](https://github.com/homarr-labs/homarr/commit/c59cdeda47a504069c09634dafe36a9bc80c922f))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.7 ([#3357](https://github.com/homarr-labs/homarr/issues/3357)) ([22f0cd8](https://github.com/homarr-labs/homarr/commit/22f0cd885e6b3f0cd15daa60840554a545eb2532))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.8 ([#3364](https://github.com/homarr-labs/homarr/issues/3364)) ([ab3ee3b](https://github.com/homarr-labs/homarr/commit/ab3ee3bac186913cbc76c1381c90b3c90a447185))
* **deps:** update dependency fast-xml-parser to ^5.2.5 ([#3353](https://github.com/homarr-labs/homarr/issues/3353)) ([9e86de6](https://github.com/homarr-labs/homarr/commit/9e86de6e1781ef2c088f83af1ba341baecb496da))
* **deps:** update dependency glob to ^11.0.3 ([#3391](https://github.com/homarr-labs/homarr/issues/3391)) ([f16efac](https://github.com/homarr-labs/homarr/commit/f16efac05a17b77303f4c41b8910d491d237add2))
* **deps:** update dependency sass to ^1.89.2 ([#3362](https://github.com/homarr-labs/homarr/issues/3362)) ([b1a559d](https://github.com/homarr-labs/homarr/commit/b1a559d80161f59114ba3098005c3334662987eb))
* **deps:** update dependency swagger-ui-react to ^5.24.1 ([#3368](https://github.com/homarr-labs/homarr/issues/3368)) ([fa52b55](https://github.com/homarr-labs/homarr/commit/fa52b5582880075cd76826641547ccbf42b56f7a))
* **deps:** update dependency tsx to v4.20.0 ([#3375](https://github.com/homarr-labs/homarr/issues/3375)) ([3d07306](https://github.com/homarr-labs/homarr/commit/3d07306e7d3bf90db7f7c02d68550a137c5554bc))
* **deps:** update dependency tsx to v4.20.1 ([#3380](https://github.com/homarr-labs/homarr/issues/3380)) ([e7652ea](https://github.com/homarr-labs/homarr/commit/e7652ea4eb1ddaf8373c02abfcdb5440bf55c236))
* **deps:** update dependency tsx to v4.20.2 ([#3387](https://github.com/homarr-labs/homarr/issues/3387)) ([79519ec](https://github.com/homarr-labs/homarr/commit/79519ec0ebf4ff101fa1cd395f974e023d1af3c3))
* **deps:** update dependency tsx to v4.20.3 ([#3393](https://github.com/homarr-labs/homarr/issues/3393)) ([c9097a9](https://github.com/homarr-labs/homarr/commit/c9097a95e2072ed619e93301f8d4644aaadce2dd))
* **deps:** update dependency typescript-eslint to ^8.34.0 ([#3359](https://github.com/homarr-labs/homarr/issues/3359)) ([f6da091](https://github.com/homarr-labs/homarr/commit/f6da0915773dd60b40475f258d90e5f3dbdb6729))
* **deps:** update dependency zod to ^3.25.56 ([#3340](https://github.com/homarr-labs/homarr/issues/3340)) ([aafd883](https://github.com/homarr-labs/homarr/commit/aafd883c3250ddf3438877d82b4b63a6a15eadda))
* **deps:** update dependency zod to ^3.25.57 ([#3367](https://github.com/homarr-labs/homarr/issues/3367)) ([e7a9797](https://github.com/homarr-labs/homarr/commit/e7a9797484e9e2cd595d31e6db4cdf63e25e3b82))
* **deps:** update dependency zod to ^3.25.58 ([#3373](https://github.com/homarr-labs/homarr/issues/3373)) ([f37fc0e](https://github.com/homarr-labs/homarr/commit/f37fc0edfee45386b3469bac11c91e4f17c83673))
* **deps:** update dependency zod to ^3.25.60 ([#3378](https://github.com/homarr-labs/homarr/issues/3378)) ([7388c90](https://github.com/homarr-labs/homarr/commit/7388c907a57c7999a48118d96a3f34d2c3656f79))
* **deps:** update dependency zod to ^3.25.61 ([#3379](https://github.com/homarr-labs/homarr/issues/3379)) ([863f935](https://github.com/homarr-labs/homarr/commit/863f935ef4c27f87a632a5e0b376fe088f4e8a04))
* **deps:** update dependency zod to ^3.25.62 ([#3382](https://github.com/homarr-labs/homarr/issues/3382)) ([0b200dc](https://github.com/homarr-labs/homarr/commit/0b200dc9e2239060ddf2670f8508f68e624e821e))
* **deps:** update dependency zod to ^3.25.63 ([#3386](https://github.com/homarr-labs/homarr/issues/3386)) ([153c2ef](https://github.com/homarr-labs/homarr/commit/153c2ef8a0098cbe2d998c98a25a0c5880f0da5d))
* **deps:** update dependency zod to ^3.25.64 ([#3392](https://github.com/homarr-labs/homarr/issues/3392)) ([5dc0ee8](https://github.com/homarr-labs/homarr/commit/5dc0ee8842dbd4bde468c5762cf5dc0d73958242))
* **deps:** update dependency zod-validation-error to ^3.5.0 ([#3388](https://github.com/homarr-labs/homarr/issues/3388)) ([747d034](https://github.com/homarr-labs/homarr/commit/747d034114bd2a3b381aa7318c59015b21811353))
* **deps:** update mantine monorepo to ^8.1.0 ([#3360](https://github.com/homarr-labs/homarr/issues/3360)) ([d862ae0](https://github.com/homarr-labs/homarr/commit/d862ae03e319d173468cd7797bd74be022cbc966))
* **deps:** update tanstack-query monorepo to ^5.80.7 ([#3385](https://github.com/homarr-labs/homarr/issues/3385)) ([c5cc8f1](https://github.com/homarr-labs/homarr/commit/c5cc8f1d375d5a582b169da706a5b433a0181c20))
* **deps:** update testcontainers-node monorepo to ^11.0.1 ([#3343](https://github.com/homarr-labs/homarr/issues/3343)) ([6ec66b1](https://github.com/homarr-labs/homarr/commit/6ec66b1fe2d5779b2267532d75d6933b3460ffd5))
* **deps:** update testcontainers-node monorepo to ^11.0.2 ([#3352](https://github.com/homarr-labs/homarr/issues/3352)) ([98c4cd1](https://github.com/homarr-labs/homarr/commit/98c4cd1e39286164c09f7d8e961488e26fa2c562))
* **deps:** update testcontainers-node monorepo to ^11.0.3 ([#3369](https://github.com/homarr-labs/homarr/issues/3369)) ([ad444e8](https://github.com/homarr-labs/homarr/commit/ad444e8fa7a186710deafd98f2aa48717de1ec80))
* **deps:** update trpc monorepo to ^11.4.0 ([#3389](https://github.com/homarr-labs/homarr/issues/3389)) ([3bf4f9e](https://github.com/homarr-labs/homarr/commit/3bf4f9e0588440d74dfaf7016acc66c1ed40843a))
* **deps:** update trpc monorepo to ^11.4.1 ([#3395](https://github.com/homarr-labs/homarr/issues/3395)) ([6d9992d](https://github.com/homarr-labs/homarr/commit/6d9992d7114f97c24e02c40dcaf11fad90608a1c))
* module not found sshcrypto.node during development ([#3349](https://github.com/homarr-labs/homarr/issues/3349)) ([1b3770a](https://github.com/homarr-labs/homarr/commit/1b3770aafbe4e903e6235dc681edac556a36e4e0))
* **pi-hole:** cached session not invalidated when changing password ([#3351](https://github.com/homarr-labs/homarr/issues/3351)) ([a9e68e1](https://github.com/homarr-labs/homarr/commit/a9e68e13d53c8e06d150ecba085680cd248d4f64))
## [1.23.0](https://github.com/homarr-labs/homarr/compare/v1.22.0...v1.23.0) (2025-06-06)
### Features
* **releases-widget:** add `import from docker` functionality ([#3130](https://github.com/homarr-labs/homarr/issues/3130)) ([7071d76](https://github.com/homarr-labs/homarr/commit/7071d76c526f1b59a955abcc595894975d574218))
### Bug Fixes
* Allow nullable on cpuModelName and default to "Unknown CPU" if null in OMV Integration ([#3310](https://github.com/homarr-labs/homarr/issues/3310)) ([3e7bf62](https://github.com/homarr-labs/homarr/commit/3e7bf62500f4ce0a8662104e2a656ea98cf2558f))
* csp prevents external stylesheets ([#3337](https://github.com/homarr-labs/homarr/issues/3337)) ([c7a21b1](https://github.com/homarr-labs/homarr/commit/c7a21b1a5f4e7c9d994d81b75a3fd4fa7c013daf))
* **deps:** update dependency @tabler/icons-react to ^3.34.0 ([#3303](https://github.com/homarr-labs/homarr/issues/3303)) ([5b354ac](https://github.com/homarr-labs/homarr/commit/5b354aca2047e8316b331e327cf6031690dcfef9))
* **deps:** update dependency dockerode to ^4.0.7 ([#3331](https://github.com/homarr-labs/homarr/issues/3331)) ([07f81ce](https://github.com/homarr-labs/homarr/commit/07f81cee92627e3b6ee4b2bbf084657f9a83ac34))
* **deps:** update dependency drizzle-orm to ^0.44.1 ([#3280](https://github.com/homarr-labs/homarr/issues/3280)) ([9895c1b](https://github.com/homarr-labs/homarr/commit/9895c1b3ff028a7ea7273ab2625620c9132f5213))
* **deps:** update dependency drizzle-orm to ^0.44.2 ([#3315](https://github.com/homarr-labs/homarr/issues/3315)) ([9ac9042](https://github.com/homarr-labs/homarr/commit/9ac90425647bf1f10d3f299f043c5d40067347bc))
* **deps:** update dependency fast-xml-parser to ^5.2.4 ([#3336](https://github.com/homarr-labs/homarr/issues/3336)) ([3ad72e6](https://github.com/homarr-labs/homarr/commit/3ad72e63b1adecd18c53897a6023f09d6f5d011c))
* **deps:** update dependency node-cron to ^4.1.0 ([#3286](https://github.com/homarr-labs/homarr/issues/3286)) ([1f40868](https://github.com/homarr-labs/homarr/commit/1f408685e69397fa9e3a69bd4331a8240da3713b))
* **deps:** update dependency sass to ^1.89.1 ([#3285](https://github.com/homarr-labs/homarr/issues/3285)) ([b608ffe](https://github.com/homarr-labs/homarr/commit/b608ffe4900a5dd97ceb2669464c477404d090e7))
* **deps:** update dependency swagger-ui-react to ^5.24.0 ([#3325](https://github.com/homarr-labs/homarr/issues/3325)) ([cf7336b](https://github.com/homarr-labs/homarr/commit/cf7336ba87c617578ab52e2ece94fd84df3fc623))
* **deps:** update dependency typescript-eslint to ^8.33.1 ([#3302](https://github.com/homarr-labs/homarr/issues/3302)) ([124e8de](https://github.com/homarr-labs/homarr/commit/124e8de6c584b0efca91d92079f8b3c400bccab2))
* **deps:** update dependency video.js to ^8.23.3 ([#3320](https://github.com/homarr-labs/homarr/issues/3320)) ([81509fd](https://github.com/homarr-labs/homarr/commit/81509fd96dfe0561964eb58d3d21c02400db2e8c))
* **deps:** update dependency zod to ^3.25.43 ([#3290](https://github.com/homarr-labs/homarr/issues/3290)) ([161fc86](https://github.com/homarr-labs/homarr/commit/161fc867703c4cf634eaaeec1b73f5e3cbfa7fec))
* **deps:** update dependency zod to ^3.25.45 ([#3291](https://github.com/homarr-labs/homarr/issues/3291)) ([3337cfa](https://github.com/homarr-labs/homarr/commit/3337cfa3c7e34debcccdbb95ee0bc39f2af98a91))
* **deps:** update dependency zod to ^3.25.46 ([#3292](https://github.com/homarr-labs/homarr/issues/3292)) ([1b1671e](https://github.com/homarr-labs/homarr/commit/1b1671e0e6d808469ba7f8185c4590cf2f2f6f44))
* **deps:** update dependency zod to ^3.25.47 ([#3296](https://github.com/homarr-labs/homarr/issues/3296)) ([94f176d](https://github.com/homarr-labs/homarr/commit/94f176d544272e0378e65255aba58b05d56019b2))
* **deps:** update dependency zod to ^3.25.48 ([#3297](https://github.com/homarr-labs/homarr/issues/3297)) ([ccd8a85](https://github.com/homarr-labs/homarr/commit/ccd8a85e0fcd2cf40dbfb6058787d24a02680b93))
* **deps:** update dependency zod to ^3.25.49 ([#3304](https://github.com/homarr-labs/homarr/issues/3304)) ([0362487](https://github.com/homarr-labs/homarr/commit/0362487156457ae825cac501e7d53db58c31ab0b))
* **deps:** update dependency zod to ^3.25.50 ([#3312](https://github.com/homarr-labs/homarr/issues/3312)) ([a0a0449](https://github.com/homarr-labs/homarr/commit/a0a0449c704ff57725ab5e0b459eb209fc929446))
* **deps:** update dependency zod to ^3.25.51 ([#3316](https://github.com/homarr-labs/homarr/issues/3316)) ([a542ee0](https://github.com/homarr-labs/homarr/commit/a542ee079871c7609ed97c76aa2017305adbfdc9))
* **deps:** update dependency zod to ^3.25.55 ([#3332](https://github.com/homarr-labs/homarr/issues/3332)) ([ed3f848](https://github.com/homarr-labs/homarr/commit/ed3f848e8afb57cc1b9249551f0260eb52bbd4a6))
* **deps:** update tanstack-query monorepo to ^5.79.1 ([#3300](https://github.com/homarr-labs/homarr/issues/3300)) ([a3deed3](https://github.com/homarr-labs/homarr/commit/a3deed3b090aed0df0f45a9bd04c006640691e05))
* **deps:** update tanstack-query monorepo to ^5.79.2 ([#3301](https://github.com/homarr-labs/homarr/issues/3301)) ([a0585e3](https://github.com/homarr-labs/homarr/commit/a0585e3dd7d9cfbccf8ff5535ae2c753a8b92764))
* **deps:** update tanstack-query monorepo to ^5.80.0 ([#3307](https://github.com/homarr-labs/homarr/issues/3307)) ([df2a9a5](https://github.com/homarr-labs/homarr/commit/df2a9a5329e06847904af56a0d058c0da59f6380))
* **deps:** update tanstack-query monorepo to ^5.80.2 ([#3308](https://github.com/homarr-labs/homarr/issues/3308)) ([cae0cad](https://github.com/homarr-labs/homarr/commit/cae0cad92f68f716887e077194d4e30402466d57))
* **deps:** update tanstack-query monorepo to ^5.80.3 ([#3317](https://github.com/homarr-labs/homarr/issues/3317)) ([c051a42](https://github.com/homarr-labs/homarr/commit/c051a42791de4eae4baf20360efe5b0c8925c22b))
* **deps:** update tanstack-query monorepo to ^5.80.5 ([#3321](https://github.com/homarr-labs/homarr/issues/3321)) ([cb77a7d](https://github.com/homarr-labs/homarr/commit/cb77a7dc6d60560250765d129aac9bd236693e2b))
* **deps:** update tanstack-query monorepo to ^5.80.6 ([#3326](https://github.com/homarr-labs/homarr/issues/3326)) ([61553cf](https://github.com/homarr-labs/homarr/commit/61553cfc8e48a1179a148007fb970b1f4e6d509c))
* **deps:** update testcontainers-node monorepo to v11 (major) ([#3250](https://github.com/homarr-labs/homarr/issues/3250)) ([0d1e3a6](https://github.com/homarr-labs/homarr/commit/0d1e3a625a04abd5f667640ab40e01d28e017488))
* **deps:** update tiptap monorepo to v2.13.0 ([#3323](https://github.com/homarr-labs/homarr/issues/3323)) ([dac8234](https://github.com/homarr-labs/homarr/commit/dac82347e86dd7faacb24adbfc61ace9c917b8cd))
* **deps:** update tiptap monorepo to v2.14.0 ([#3334](https://github.com/homarr-labs/homarr/issues/3334)) ([6274a66](https://github.com/homarr-labs/homarr/commit/6274a66048163513e3faaf7011d7d6aa6ad9f161))
* **deps:** update trpc monorepo to ^11.2.0 ([#3293](https://github.com/homarr-labs/homarr/issues/3293)) ([f0ba693](https://github.com/homarr-labs/homarr/commit/f0ba693077a0c7f7897c81f02ad66b77fce52f1c))
* **deps:** update trpc monorepo to ^11.3.0 ([#3322](https://github.com/homarr-labs/homarr/issues/3322)) ([74aa561](https://github.com/homarr-labs/homarr/commit/74aa561f470f0f76421f356998caa91dc8bb65a1))
* **deps:** update trpc monorepo to ^11.3.1 ([#3329](https://github.com/homarr-labs/homarr/issues/3329)) ([8484daa](https://github.com/homarr-labs/homarr/commit/8484daaf7516ad72dd9e7de3117b5e0d50415dbb))
* **nextcloud:** correct calendar event date mapping and support showU… ([#3330](https://github.com/homarr-labs/homarr/issues/3330)) ([ffd2f80](https://github.com/homarr-labs/homarr/commit/ffd2f802bb777f13eac1ee45e1b5f0ca2b596672))
## [1.22.0](https://github.com/homarr-labs/homarr/compare/v1.21.0...v1.22.0) (2025-05-30)
### Features
* allow custom protocols for app href ([#2575](https://github.com/homarr-labs/homarr/issues/2575)) ([09af0a7](https://github.com/homarr-labs/homarr/commit/09af0a76f64c2fec512ba30726827208d5c3ad95))
* **cluster-health:** add visibility options ([#3210](https://github.com/homarr-labs/homarr/issues/3210)) ([939df8f](https://github.com/homarr-labs/homarr/commit/939df8f6d11bb36f71854425e4fdc4ca0724da31))
* **downloads:** add option to limit amount of items ([#3205](https://github.com/homarr-labs/homarr/issues/3205)) ([2dc871e](https://github.com/homarr-labs/homarr/commit/2dc871e531b58aee25db26ab7c5e94626517db36))
### Bug Fixes
* **boards:** app select modal images rendering issue ([#3218](https://github.com/homarr-labs/homarr/issues/3218)) ([85e98d2](https://github.com/homarr-labs/homarr/commit/85e98d2b603abe18b37fa74bf50836422b72c9e1))
* **calendar:** load events for all visible days of calendar month ([#3216](https://github.com/homarr-labs/homarr/issues/3216)) ([437ecd1](https://github.com/homarr-labs/homarr/commit/437ecd10239c70e5a8c4d34119076ab155a2159e))
* **calendar:** remove scrollbar for 2x2 ([#3227](https://github.com/homarr-labs/homarr/issues/3227)) ([1d27da2](https://github.com/homarr-labs/homarr/commit/1d27da22303b2133bb1a7c59102b7c292bc34833))
* **cluster-health:** storage items multiply ([#3206](https://github.com/homarr-labs/homarr/issues/3206)) ([1114934](https://github.com/homarr-labs/homarr/commit/11149348fcbe9002bba33763e0b03aed50d20f71))
* csp prevents fonts to load ([#3225](https://github.com/homarr-labs/homarr/issues/3225)) ([05e159f](https://github.com/homarr-labs/homarr/commit/05e159f5662f04dd298eb13816fe1768d7ef9312))
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.4.2 ([#3258](https://github.com/homarr-labs/homarr/issues/3258)) ([4a184d2](https://github.com/homarr-labs/homarr/commit/4a184d23f25a8a06b36cd8c6d9b2a65118399d9f))
* **deps:** update dependency @kubernetes/client-node to ^1.3.0 ([#3261](https://github.com/homarr-labs/homarr/issues/3261)) ([635c9a2](https://github.com/homarr-labs/homarr/commit/635c9a2687cf2b9a09a3093eb638ef772eddfb03))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.6 ([#3244](https://github.com/homarr-labs/homarr/issues/3244)) ([8966e2a](https://github.com/homarr-labs/homarr/commit/8966e2ad8a2025dfc91934ccf825bd76c08dc333))
* **deps:** update dependency drizzle-orm to ^0.44.0 ([#3256](https://github.com/homarr-labs/homarr/issues/3256)) ([09b49a2](https://github.com/homarr-labs/homarr/commit/09b49a242dd5d3d34bab758f190ab948b6fc7566))
* **deps:** update dependency flag-icons to ^7.4.0 ([#3265](https://github.com/homarr-labs/homarr/issues/3265)) ([a0eda74](https://github.com/homarr-labs/homarr/commit/a0eda74fa472446c5b93c1b65829e65670620ab6))
* **deps:** update dependency flag-icons to ^7.5.0 ([#3268](https://github.com/homarr-labs/homarr/issues/3268)) ([2320d3c](https://github.com/homarr-labs/homarr/commit/2320d3c5674413e18e9bb42fd004ada016c5e605))
* **deps:** update dependency jotai to ^2.12.5 ([#3243](https://github.com/homarr-labs/homarr/issues/3243)) ([9c32385](https://github.com/homarr-labs/homarr/commit/9c323850bc49df5f6fc3437678faab0c7eab2e53))
* **deps:** update dependency ldapts to v8.0.1 ([#3264](https://github.com/homarr-labs/homarr/issues/3264)) ([bac4f64](https://github.com/homarr-labs/homarr/commit/bac4f642574caf5f170d20353c036396698e5963))
* **deps:** update dependency node-cron to v4 ([#3075](https://github.com/homarr-labs/homarr/issues/3075)) ([109f9e7](https://github.com/homarr-labs/homarr/commit/109f9e79793b36b46ab1987a56f7c206e21d32e4))
* **deps:** update dependency octokit to ^5.0.3 ([#3249](https://github.com/homarr-labs/homarr/issues/3249)) ([3253b2f](https://github.com/homarr-labs/homarr/commit/3253b2f138a9e911b50c952e37fb4afb1f71446d))
* **deps:** update dependency trpc-to-openapi to ^2.3.1 ([#3046](https://github.com/homarr-labs/homarr/issues/3046)) ([640bf7b](https://github.com/homarr-labs/homarr/commit/640bf7b939b201d9905f5119af0dc0da6120b98c))
* **deps:** update dependency typescript-eslint to ^8.33.0 ([#3247](https://github.com/homarr-labs/homarr/issues/3247)) ([8b553b8](https://github.com/homarr-labs/homarr/commit/8b553b80e61ab608d01d890d26a8ea5bae3f38b8))
* **deps:** update dependency zod to ^3.25.24 ([#3195](https://github.com/homarr-labs/homarr/issues/3195)) ([0b224e8](https://github.com/homarr-labs/homarr/commit/0b224e8835d4fc5a292a624c8af8d874216b703a))
* **deps:** update dependency zod to ^3.25.28 ([#3201](https://github.com/homarr-labs/homarr/issues/3201)) ([e0a9e4f](https://github.com/homarr-labs/homarr/commit/e0a9e4f97c9dddb95fbc0b2a65acfd802dac9c19))
* **deps:** update dependency zod to ^3.25.29 ([#3237](https://github.com/homarr-labs/homarr/issues/3237)) ([81ca6ef](https://github.com/homarr-labs/homarr/commit/81ca6ef7b2767fb0bc285ffa53998e2c586225ec))
* **deps:** update dependency zod to ^3.25.30 ([#3239](https://github.com/homarr-labs/homarr/issues/3239)) ([3062948](https://github.com/homarr-labs/homarr/commit/30629489396668125dec7d93fcb0e733099c35fe))
* **deps:** update dependency zod to ^3.25.31 ([#3254](https://github.com/homarr-labs/homarr/issues/3254)) ([e2e4656](https://github.com/homarr-labs/homarr/commit/e2e46560010a94603852ea90a0fc63fabd473198))
* **deps:** update dependency zod to ^3.25.32 ([#3255](https://github.com/homarr-labs/homarr/issues/3255)) ([9c3a46c](https://github.com/homarr-labs/homarr/commit/9c3a46c071e169459ec7b8c9ba1647b4b354cbea))
* **deps:** update dependency zod to ^3.25.34 ([#3259](https://github.com/homarr-labs/homarr/issues/3259)) ([c1324ea](https://github.com/homarr-labs/homarr/commit/c1324eab76795dabc0a41315191f9ab6a73ba501))
* **deps:** update dependency zod to ^3.25.36 ([#3262](https://github.com/homarr-labs/homarr/issues/3262)) ([48ce2af](https://github.com/homarr-labs/homarr/commit/48ce2af4c182985a7721b65535bcf1b41f1cdcba))
* **deps:** update dependency zod to ^3.25.39 ([#3270](https://github.com/homarr-labs/homarr/issues/3270)) ([862d07d](https://github.com/homarr-labs/homarr/commit/862d07db68e7624342cb81241ef9924e6407fda8))
* **deps:** update dependency zod to ^3.25.41 ([#3272](https://github.com/homarr-labs/homarr/issues/3272)) ([5bf20aa](https://github.com/homarr-labs/homarr/commit/5bf20aa3538503de1d4f4d04f9de3e3b92442d75))
* **deps:** update dependency zod to ^3.25.42 ([#3274](https://github.com/homarr-labs/homarr/issues/3274)) ([608280e](https://github.com/homarr-labs/homarr/commit/608280e670432098ce466cfb8fa2b315bf35ead2))
* **deps:** update mantine monorepo to ^8.0.2 ([#3245](https://github.com/homarr-labs/homarr/issues/3245)) ([816544a](https://github.com/homarr-labs/homarr/commit/816544a996656ccf7cbacf76aff83d013ba21ad1))
* **deps:** update nextjs monorepo to v15.3.3 ([#3269](https://github.com/homarr-labs/homarr/issues/3269)) ([d72a804](https://github.com/homarr-labs/homarr/commit/d72a804ad9feb24a2b067712128a6566930990a6))
* **deps:** update tanstack-query monorepo to ^5.77.0 ([#3204](https://github.com/homarr-labs/homarr/issues/3204)) ([ec42f26](https://github.com/homarr-labs/homarr/commit/ec42f265a04f0f96105d6b1318becd5cc94aa8fb))
* **deps:** update tanstack-query monorepo to ^5.77.1 ([#3229](https://github.com/homarr-labs/homarr/issues/3229)) ([5ea7a20](https://github.com/homarr-labs/homarr/commit/5ea7a206772924faf1f842c80033954c853b3a02))
* **deps:** update tanstack-query monorepo to ^5.77.2 ([#3232](https://github.com/homarr-labs/homarr/issues/3232)) ([7118690](https://github.com/homarr-labs/homarr/commit/71186908b96f717c6f8c528b0f983e1792a7a885))
* **deps:** update tanstack-query monorepo to ^5.79.0 ([#3263](https://github.com/homarr-labs/homarr/issues/3263)) ([1f9983f](https://github.com/homarr-labs/homarr/commit/1f9983f16db8677b8c244b63306ea4afd2195d94))
* **deps:** update trpc monorepo to ^11.1.3 ([#3240](https://github.com/homarr-labs/homarr/issues/3240)) ([78f98ed](https://github.com/homarr-labs/homarr/commit/78f98edc4145e27e2daa269121173905c642c139))
* **deps:** update trpc monorepo to ^11.1.4 ([#3260](https://github.com/homarr-labs/homarr/issues/3260)) ([bfd7b70](https://github.com/homarr-labs/homarr/commit/bfd7b706182340eaa1db70a3c6cdcc935aee8df4))
* **deps:** update turbo monorepo to ^2.5.4 ([#3279](https://github.com/homarr-labs/homarr/issues/3279)) ([c8202ab](https://github.com/homarr-labs/homarr/commit/c8202ab3c676bf796c6e7aff2ebfef224a0124c8))
* **downloads:** upspeed font size wrong ([#3214](https://github.com/homarr-labs/homarr/issues/3214)) ([c67916f](https://github.com/homarr-labs/homarr/commit/c67916f72eded64133fd458184a599f32d8fc63c))
* **media-request-stats:** users squeezed together ([#3213](https://github.com/homarr-labs/homarr/issues/3213)) ([25bd387](https://github.com/homarr-labs/homarr/commit/25bd3879db9260bbd24a1338cc5f26652ed78d72))
* navigating back results in navigation back to first page opened ([#3224](https://github.com/homarr-labs/homarr/issues/3224)) ([cbbf0c8](https://github.com/homarr-labs/homarr/commit/cbbf0c8dcf685e31e70bb62f04b1065dab90e18d))
* reduce logging ([#3200](https://github.com/homarr-labs/homarr/issues/3200)) ([be8b332](https://github.com/homarr-labs/homarr/commit/be8b332f942beb3af181ee6f39558882ed640a0e))
* **search:** removing server search engine causes loading error ([#3252](https://github.com/homarr-labs/homarr/issues/3252)) ([10f720d](https://github.com/homarr-labs/homarr/commit/10f720d74c6cc70cbf217e8daf8cc7878ac6d712))
* **visual:** check for creator board permission is wrong ([#3212](https://github.com/homarr-labs/homarr/issues/3212)) ([f7e5e82](https://github.com/homarr-labs/homarr/commit/f7e5e823d5dee36ea11f243a0da7d84a1f363419))
### Performance Improvements
* cache middleware onboarding and language checks ([#3197](https://github.com/homarr-labs/homarr/issues/3197)) ([2afa9b6](https://github.com/homarr-labs/homarr/commit/2afa9b67274edd79b52598dc0d22437048a67e2c))
## [1.21.0](https://github.com/homarr-labs/homarr/compare/v1.20.0...v1.21.0) (2025-05-23)
### Features
* docker widget ([#2288](https://github.com/homarr-labs/homarr/issues/2288)) ([e1eda53](https://github.com/homarr-labs/homarr/commit/e1eda534da6d97655546f3140f9e36eca8817db3))
### Bug Fixes
* **deps:** update dependency @tabler/icons-react to ^3.33.0 ([#3132](https://github.com/homarr-labs/homarr/issues/3132)) ([ee8870b](https://github.com/homarr-labs/homarr/commit/ee8870b66bbcb8eeeef359ece9391a496198be0d))
* **deps:** update dependency octokit to ^4.1.4 ([#3165](https://github.com/homarr-labs/homarr/issues/3165)) ([50445b5](https://github.com/homarr-labs/homarr/commit/50445b560edc3af2631f8d8d9aa81f3387d848f7))
* **deps:** update dependency octokit to v5 ([#3156](https://github.com/homarr-labs/homarr/issues/3156)) ([8c59abd](https://github.com/homarr-labs/homarr/commit/8c59abdde3d8034f22a2b926b3291b55a0d492ea))
* **deps:** update dependency swagger-ui-react to ^5.22.0 ([#3178](https://github.com/homarr-labs/homarr/issues/3178)) ([1e7b2e6](https://github.com/homarr-labs/homarr/commit/1e7b2e617b1437f0f98bc791e763bc49b6589b9e))
* **deps:** update dependency undici to v7.10.0 ([#3158](https://github.com/homarr-labs/homarr/issues/3158)) ([eb5d5f8](https://github.com/homarr-labs/homarr/commit/eb5d5f8260cc9795806ecdb56fbb3f8d19858ed7))
* **deps:** update dependency zod to ^3.25.12 ([#3172](https://github.com/homarr-labs/homarr/issues/3172)) ([caaee50](https://github.com/homarr-labs/homarr/commit/caaee503c128ff2a0b2a7de7a7c353484e4741a5))
* **deps:** update dependency zod to ^3.25.13 ([#3173](https://github.com/homarr-labs/homarr/issues/3173)) ([dae6ded](https://github.com/homarr-labs/homarr/commit/dae6dedde544bf28fcdf87c71a9d585078527b82))
* **deps:** update dependency zod to ^3.25.14 ([#3174](https://github.com/homarr-labs/homarr/issues/3174)) ([23404a2](https://github.com/homarr-labs/homarr/commit/23404a27f1a9dfec4e9126750f8d97e7c5eda572))
* **deps:** update dependency zod to ^3.25.16 ([#3175](https://github.com/homarr-labs/homarr/issues/3175)) ([5080f51](https://github.com/homarr-labs/homarr/commit/5080f51a8dbe1b82571f356f7ae29ed7be69e18e))
* **deps:** update dependency zod to ^3.25.17 ([#3177](https://github.com/homarr-labs/homarr/issues/3177)) ([371803a](https://github.com/homarr-labs/homarr/commit/371803ac509efd246367c812a6c0143e7aeda03f))
* **deps:** update dependency zod to ^3.25.20 ([#3183](https://github.com/homarr-labs/homarr/issues/3183)) ([49498c8](https://github.com/homarr-labs/homarr/commit/49498c870e494b417e6c63d8547a4c0ffd93768b))
* **deps:** update dependency zod to ^3.25.21 ([#3187](https://github.com/homarr-labs/homarr/issues/3187)) ([37d637d](https://github.com/homarr-labs/homarr/commit/37d637dcae6cbf62efda25a8dc01eedba66f2c74))
* **deps:** update dependency zod to ^3.25.23 ([#3188](https://github.com/homarr-labs/homarr/issues/3188)) ([9840644](https://github.com/homarr-labs/homarr/commit/9840644ca50d3157223e7da0cf895f2e750afc58))
* **deps:** update dependency zod to ^3.25.3 ([#3148](https://github.com/homarr-labs/homarr/issues/3148)) ([6e2aa87](https://github.com/homarr-labs/homarr/commit/6e2aa87102b72319236483b9a1d3e5ddd6fe2c82))
* **deps:** update dependency zod to ^3.25.4 ([#3150](https://github.com/homarr-labs/homarr/issues/3150)) ([49b7080](https://github.com/homarr-labs/homarr/commit/49b7080415aaeee576507d48e8cda18acc9943a8))
* **deps:** update dependency zod to ^3.25.5 ([#3152](https://github.com/homarr-labs/homarr/issues/3152)) ([79755c9](https://github.com/homarr-labs/homarr/commit/79755c95d3cf58680e434f0113c130db9f649d96))
* **deps:** update dependency zod to ^3.25.6 ([#3153](https://github.com/homarr-labs/homarr/issues/3153)) ([92ebc05](https://github.com/homarr-labs/homarr/commit/92ebc05fbfded6d20d242ad477cb0dc30243e8cc))
* **deps:** update dependency zod to ^3.25.7 ([#3154](https://github.com/homarr-labs/homarr/issues/3154)) ([22954b8](https://github.com/homarr-labs/homarr/commit/22954b8d2637cdea9a6502cab2d3d40359fa098f))
* **deps:** update dependency zod to ^3.25.8 ([#3170](https://github.com/homarr-labs/homarr/issues/3170)) ([72bcbe8](https://github.com/homarr-labs/homarr/commit/72bcbe8effe47fdfb6a94de8defc0d3c12e7c754))
* **deps:** update dependency zod-validation-error to ^3.4.1 ([#3127](https://github.com/homarr-labs/homarr/issues/3127)) ([886e95d](https://github.com/homarr-labs/homarr/commit/886e95d51714ce20e03d32b4c2cfb673e7e4c218))
* **deps:** update tanstack-query monorepo to ^5.76.2 ([#3186](https://github.com/homarr-labs/homarr/issues/3186)) ([1584114](https://github.com/homarr-labs/homarr/commit/1584114e72246a018b9f49a859e49f7fb985009d))
* **deps:** update testcontainers-node monorepo to ^10.27.0 ([#3159](https://github.com/homarr-labs/homarr/issues/3159)) ([18c465e](https://github.com/homarr-labs/homarr/commit/18c465e81e92cf58b7e32bcb70f1ab404a03a135))
* **deps:** update testcontainers-node monorepo to ^10.28.0 ([#3191](https://github.com/homarr-labs/homarr/issues/3191)) ([43b8241](https://github.com/homarr-labs/homarr/commit/43b82419e9396f0b80bafa0a707d575c3732dc5b))
* **downloads:** reordering columns in widget does not work ([#3194](https://github.com/homarr-labs/homarr/issues/3194)) ([09f4e67](https://github.com/homarr-labs/homarr/commit/09f4e6785b2ce38da3b52003e9c9300cdd0feb85))
* esbuild not part of dependencies ([#3139](https://github.com/homarr-labs/homarr/issues/3139)) ([ef540e9](https://github.com/homarr-labs/homarr/commit/ef540e980f90f730020ef081dd105e33b0671232))
* **items:** resize handle overlaps header ([#3193](https://github.com/homarr-labs/homarr/issues/3193)) ([09f3989](https://github.com/homarr-labs/homarr/commit/09f398948348fb402600506571a967ba405fbafb))
* **releases-widget:** add hardcoded css classNames([#3181](https://github.com/homarr-labs/homarr/issues/3181)) ([4cb004f](https://github.com/homarr-labs/homarr/commit/4cb004fce0750da339f603b944de55135faa4bfa))
## [1.20.0](https://github.com/homarr-labs/homarr/compare/v1.19.1...v1.20.0) (2025-05-16)
### Features
* add css class to logo for custom css ([#3121](https://github.com/homarr-labs/homarr/issues/3121)) ([9b8320c](https://github.com/homarr-labs/homarr/commit/9b8320c10d51cc449b3fb5c1532408bff5655788))
* **auth:** add account linking for oidc providers ([#3106](https://github.com/homarr-labs/homarr/issues/3106)) ([3daf1c8](https://github.com/homarr-labs/homarr/commit/3daf1c8341ab40a580777246e742ff5019a483d2))
* **integration:** improve integration test connection ([#3005](https://github.com/homarr-labs/homarr/issues/3005)) ([ef9a5e9](https://github.com/homarr-labs/homarr/commit/ef9a5e9895d7079008046df66f6a6d538af106c9))
* **releases-widget:** added auto icon search when editing a repository ([#3087](https://github.com/homarr-labs/homarr/issues/3087)) ([9413430](https://github.com/homarr-labs/homarr/commit/94134303d7dc6f2d52e4955dcd9d1afbd8d203fb))
* **releases-widget:** limit number of displayed releases and custom name for repositories ([#2974](https://github.com/homarr-labs/homarr/issues/2974)) ([8c1b365](https://github.com/homarr-labs/homarr/commit/8c1b365733c2bccd0838cbf07aa59115bcc0c0cd))
### Bug Fixes
* **calendar:** some timezones result in offset of one day ([#3120](https://github.com/homarr-labs/homarr/issues/3120)) ([b86159b](https://github.com/homarr-labs/homarr/commit/b86159ba7c1cdaea973466bd7430fc45fd0d8317))
* **deps:** missing dependencies in definitions package for docs link codegen ([#3119](https://github.com/homarr-labs/homarr/issues/3119)) ([42e7fe3](https://github.com/homarr-labs/homarr/commit/42e7fe3273b0f0e8f67e38ac3fd758528cc47913))
* **deps:** update dependency @auth/core to ^0.39.1 ([#3072](https://github.com/homarr-labs/homarr/issues/3072)) ([0d23119](https://github.com/homarr-labs/homarr/commit/0d2311975c9062f538e7cf7166cc354bc6b21468))
* **deps:** update dependency @auth/drizzle-adapter to ^1.9.1 ([#3073](https://github.com/homarr-labs/homarr/issues/3073)) ([3f9f94e](https://github.com/homarr-labs/homarr/commit/3f9f94ea45e001d0f2c966ee21d1e148103a854e))
* **deps:** update dependency @extractus/feed-extractor to v7.1.6 ([#3105](https://github.com/homarr-labs/homarr/issues/3105)) ([d96bc89](https://github.com/homarr-labs/homarr/commit/d96bc898fdc014940207c1d7bead371ac4df33a9))
* **deps:** update dependency bcrypt to v6 ([#3083](https://github.com/homarr-labs/homarr/issues/3083)) ([5686910](https://github.com/homarr-labs/homarr/commit/5686910a4735801c40caed08a4c8b931fd04418f))
* **deps:** update dependency next-auth to v5.0.0-beta.28 ([#3074](https://github.com/homarr-labs/homarr/issues/3074)) ([98773e7](https://github.com/homarr-labs/homarr/commit/98773e7dba3f7f816a50c134cedf81cddb55e5f1))
* **deps:** update dependency sass to ^1.88.0 ([#3078](https://github.com/homarr-labs/homarr/issues/3078)) ([b0f7310](https://github.com/homarr-labs/homarr/commit/b0f7310cb79e014e58a72dcf812d3d4cc876d034))
* **deps:** update dependency sass to ^1.89.0 ([#3118](https://github.com/homarr-labs/homarr/issues/3118)) ([5b16468](https://github.com/homarr-labs/homarr/commit/5b16468cab53429d4f8cf36771ab6efafea86743))
* **deps:** update dependency typescript-eslint to ^8.32.1 ([#3093](https://github.com/homarr-labs/homarr/issues/3093)) ([42b377d](https://github.com/homarr-labs/homarr/commit/42b377d0672973cb3d7b32c0d1e2c0663d68c2a8))
* **deps:** update dependency undici to v7.9.0 ([#3077](https://github.com/homarr-labs/homarr/issues/3077)) ([01d53ee](https://github.com/homarr-labs/homarr/commit/01d53eee60f7269158565cfa46f7aebdfa8d41cf))
* **deps:** update mantine monorepo to ^8.0.1 ([#3107](https://github.com/homarr-labs/homarr/issues/3107)) ([25efe3d](https://github.com/homarr-labs/homarr/commit/25efe3d33504f8e4f1eb3f29edfdeccf4b15a8e4))
* **deps:** update tanstack-query monorepo to ^5.76.0 ([#3090](https://github.com/homarr-labs/homarr/issues/3090)) ([388bc73](https://github.com/homarr-labs/homarr/commit/388bc7392c318e05f85134cf969e10d9627c55f9))
* **deps:** update tanstack-query monorepo to ^5.76.1 ([#3101](https://github.com/homarr-labs/homarr/issues/3101)) ([c1e5d77](https://github.com/homarr-labs/homarr/commit/c1e5d77176126c6248f052d2baa3cf09837fba62))
* **deps:** update testcontainers-node monorepo to ^10.26.0 ([#3114](https://github.com/homarr-labs/homarr/issues/3114)) ([cb81efa](https://github.com/homarr-labs/homarr/commit/cb81efa42c99f4bd8024b21e0818294db8d22fec))
* **releases-widget:** delete the empty repository when canceling `Add Reposotory` ([#3082](https://github.com/homarr-labs/homarr/issues/3082)) ([72862ef](https://github.com/homarr-labs/homarr/commit/72862efae274cfc25fe656aef27f6acb2e1b0ca6))
* **releases-widget:** reduced padding for `ul` in release description ([#3085](https://github.com/homarr-labs/homarr/issues/3085)) ([593d97e](https://github.com/homarr-labs/homarr/commit/593d97e27aa85ab023935d12c8cbf534d38c288f))
* **releases-widget:** removed version regex in options overview ([#3081](https://github.com/homarr-labs/homarr/issues/3081)) ([59cae02](https://github.com/homarr-labs/homarr/commit/59cae02623b09e60b8c300c18349ea282c872822))
* **releases-widget:** validation preventing removal of a previously added icon ([#3086](https://github.com/homarr-labs/homarr/issues/3086)) ([18fe8ec](https://github.com/homarr-labs/homarr/commit/18fe8ecd39f6551135beb0333cbad5b9ff4d6664))
* updated contributors contain unnecessary data ([#3124](https://github.com/homarr-labs/homarr/issues/3124)) ([6e03e5a](https://github.com/homarr-labs/homarr/commit/6e03e5a40c9aa0ee5e0e3de4277ffbe44ad45b66))
## [1.19.1](https://github.com/homarr-labs/homarr/compare/v1.19.0...v1.19.1) (2025-05-09)
### Bug Fixes
* **deps:** update dependency @extractus/feed-extractor to v7.1.5 ([#3020](https://github.com/homarr-labs/homarr/issues/3020)) ([758b1a0](https://github.com/homarr-labs/homarr/commit/758b1a049019d903f152f783eab3b6fc4be4de14))
* **deps:** update dependency @kubernetes/client-node to ^1.2.0 ([#3042](https://github.com/homarr-labs/homarr/issues/3042)) ([273d4ed](https://github.com/homarr-labs/homarr/commit/273d4edb233f5d5faa7105c23208d95268119786))
* **deps:** update dependency better-sqlite3 to ^11.10.0 ([#3053](https://github.com/homarr-labs/homarr/issues/3053)) ([0d5f430](https://github.com/homarr-labs/homarr/commit/0d5f430bd45e6d29d604d4f93ff57cfc7a64e309))
* **deps:** update dependency drizzle-kit to ^0.31.1 ([#3013](https://github.com/homarr-labs/homarr/issues/3013)) ([76ce6ff](https://github.com/homarr-labs/homarr/commit/76ce6ff1fab34223e7ff5abf46193f1ae9b011d1))
* **deps:** update dependency eslint-config-prettier to ^10.1.3 ([#3047](https://github.com/homarr-labs/homarr/issues/3047)) ([e94c05f](https://github.com/homarr-labs/homarr/commit/e94c05f3d1eea1841b978359d3c41d8a46a08a39))
* **deps:** update dependency eslint-config-prettier to ^10.1.5 ([#3061](https://github.com/homarr-labs/homarr/issues/3061)) ([395e03c](https://github.com/homarr-labs/homarr/commit/395e03c8101af873f35388ae1b833dc68d552792))
* **deps:** update dependency jotai to ^2.12.4 ([#3038](https://github.com/homarr-labs/homarr/issues/3038)) ([7ec1793](https://github.com/homarr-labs/homarr/commit/7ec179304040d6782e516358aeaae2eaa143dc1a))
* **deps:** update dependency ldapts to v8 ([#3028](https://github.com/homarr-labs/homarr/issues/3028)) ([7c0baf5](https://github.com/homarr-labs/homarr/commit/7c0baf5fba19735c2b3fac0728359a9aa8c617e6))
* **deps:** update dependency react-error-boundary to v6 ([#3016](https://github.com/homarr-labs/homarr/issues/3016)) ([680f7b0](https://github.com/homarr-labs/homarr/commit/680f7b0c7277ffcbb6a033fd7a2f14146e8a2fca))
* **deps:** update dependency typescript-eslint to ^8.32.0 ([#3027](https://github.com/homarr-labs/homarr/issues/3027)) ([bee1ddd](https://github.com/homarr-labs/homarr/commit/bee1dddc9ff1b213ef9fd9cc1696bf22cc816b19))
* **deps:** update dependency ws to ^8.18.2 ([#3012](https://github.com/homarr-labs/homarr/issues/3012)) ([9bb26eb](https://github.com/homarr-labs/homarr/commit/9bb26ebcc3a3f400317c85bd1fb13621d52049c2))
* **deps:** update dependency zod to ^3.24.4 ([#3021](https://github.com/homarr-labs/homarr/issues/3021)) ([e5e686d](https://github.com/homarr-labs/homarr/commit/e5e686d59086320a565c3367a389d1117fd74d88))
* **deps:** update mantine monorepo to v8 (major) ([#3022](https://github.com/homarr-labs/homarr/issues/3022)) ([44853ea](https://github.com/homarr-labs/homarr/commit/44853ea64bed3a093a26cb07904d8beedf220e4e))
* **deps:** update nextjs monorepo to v15.3.2 ([#3045](https://github.com/homarr-labs/homarr/issues/3045)) ([b6eb8ca](https://github.com/homarr-labs/homarr/commit/b6eb8ca0f6d0e6b2fac40423a6c0157dff292622))
* **deps:** update tanstack-query monorepo to ^5.75.2 ([#3018](https://github.com/homarr-labs/homarr/issues/3018)) ([d512fc0](https://github.com/homarr-labs/homarr/commit/d512fc042a0cb06238f3c86f92b12ad152208a9c))
* **deps:** update tanstack-query monorepo to ^5.75.4 ([#3029](https://github.com/homarr-labs/homarr/issues/3029)) ([861728a](https://github.com/homarr-labs/homarr/commit/861728a7c3156bbe618b9fb186d9f168ba9b339b))
* **deps:** update tanstack-query monorepo to ^5.75.5 ([#3048](https://github.com/homarr-labs/homarr/issues/3048)) ([8cb9ddb](https://github.com/homarr-labs/homarr/commit/8cb9ddbce3f00e6e3df07c796c39fda7dd18b7b1))
* **deps:** update tanstack-query monorepo to ^5.75.6 ([#3062](https://github.com/homarr-labs/homarr/issues/3062)) ([d6180eb](https://github.com/homarr-labs/homarr/commit/d6180ebc7387249265ad9c296c52aab6d393d88a))
* **deps:** update tanstack-query monorepo to ^5.75.7 ([#3063](https://github.com/homarr-labs/homarr/issues/3063)) ([b71c5c6](https://github.com/homarr-labs/homarr/commit/b71c5c6fc73cab4ebc1bfe9d70722f166ebcdb47))
* **deps:** update tiptap monorepo to v2.12.0 ([#3025](https://github.com/homarr-labs/homarr/issues/3025)) ([8a0557f](https://github.com/homarr-labs/homarr/commit/8a0557f927f6423e37a372c4c4ffea224598ff45))
* **deps:** update turbo monorepo to ^2.5.3 ([#3043](https://github.com/homarr-labs/homarr/issues/3043)) ([9b6110b](https://github.com/homarr-labs/homarr/commit/9b6110b9c9ec08bde335cc95d7548e2dddfab45b))
* **logs:** log error cause when object, sign in oidc debug messages ([#3054](https://github.com/homarr-labs/homarr/issues/3054)) ([2bdb1c7](https://github.com/homarr-labs/homarr/commit/2bdb1c75f680e7715bbe68c6f50e24ea861302ca))
* **releases-widget:** error display, decouple database repository object from responses and batch widget queries ([#2891](https://github.com/homarr-labs/homarr/issues/2891)) ([347c6d1](https://github.com/homarr-labs/homarr/commit/347c6d15191a64899ff844cfe689973f44b9c556))
* **releases-widget:** improved provider parsing of missing properties ([#2964](https://github.com/homarr-labs/homarr/issues/2964)) ([b62d47d](https://github.com/homarr-labs/homarr/commit/b62d47d303ca1271b932b6347a489df6f16fc680))
## [1.19.0](https://github.com/homarr-labs/homarr/compare/v1.18.1...v1.19.0) (2025-05-02)
### Features
* **widget:** add prefetch for apps and bookmarks ([#2895](https://github.com/homarr-labs/homarr/issues/2895)) ([82c5361](https://github.com/homarr-labs/homarr/commit/82c53611122dd07e5639a902afa0a6a4fd8ed4e5))
### Bug Fixes
* **cookie:** parsing of header not working with percentage char ([#2989](https://github.com/homarr-labs/homarr/issues/2989)) ([547a8c3](https://github.com/homarr-labs/homarr/commit/547a8c34c58c88e6f0074df327f49aadf9ab07f4))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.4 ([#2993](https://github.com/homarr-labs/homarr/issues/2993)) ([1a07fa9](https://github.com/homarr-labs/homarr/commit/1a07fa9af59e93f9ce5d70b72855bb070f5b5787))
* **deps:** update dependency tsx to v4.19.4 ([#2981](https://github.com/homarr-labs/homarr/issues/2981)) ([a2dbac5](https://github.com/homarr-labs/homarr/commit/a2dbac5ad73aa50af7f4b40076bb5fcfa61adf80))
* **deps:** update dependency typescript-eslint to ^8.31.1 ([#2979](https://github.com/homarr-labs/homarr/issues/2979)) ([284ea23](https://github.com/homarr-labs/homarr/commit/284ea239f71f49c74e3ea60fa5de2fd968f1675b))
* **deps:** update mantine monorepo to ^7.17.7 ([#3004](https://github.com/homarr-labs/homarr/issues/3004)) ([a70b0b7](https://github.com/homarr-labs/homarr/commit/a70b0b70479df8fff8a2d256995844bad1fb55a0))
* **deps:** update tanstack-query monorepo to ^5.74.11 ([#2984](https://github.com/homarr-labs/homarr/issues/2984)) ([3299ced](https://github.com/homarr-labs/homarr/commit/3299cedc04cd87c7f014f69a900e7c6e6c33d954))
* **deps:** update tanstack-query monorepo to ^5.74.7 ([#2924](https://github.com/homarr-labs/homarr/issues/2924)) ([f4ce76a](https://github.com/homarr-labs/homarr/commit/f4ce76ac13136c2f559d3aff019dcc2add7c98dd))
* **deps:** update tanstack-query monorepo to ^5.74.9 ([#2983](https://github.com/homarr-labs/homarr/issues/2983)) ([1012cad](https://github.com/homarr-labs/homarr/commit/1012cad0e32f43f46bbb4e89ade3f0eb7fa54313))
* **deps:** update tanstack-query monorepo to ^5.75.0 ([#2998](https://github.com/homarr-labs/homarr/issues/2998)) ([0d1b40c](https://github.com/homarr-labs/homarr/commit/0d1b40c381fc068c770480f81c6c7dcefce0ec86))
* **deps:** update tanstack-query monorepo to ^5.75.1 ([#3006](https://github.com/homarr-labs/homarr/issues/3006)) ([2b1c433](https://github.com/homarr-labs/homarr/commit/2b1c433cefe667d930ce5178cc1218ffb7fa43c0))
* **deps:** update testcontainers-node monorepo to ^10.25.0 ([#2997](https://github.com/homarr-labs/homarr/issues/2997)) ([a934529](https://github.com/homarr-labs/homarr/commit/a934529146a0f53a047d2e3e834145c62c8ee891))
* **deps:** update tiptap monorepo to v2.11.9 ([#2987](https://github.com/homarr-labs/homarr/issues/2987)) ([70686a0](https://github.com/homarr-labs/homarr/commit/70686a00662a7f690efbb45920e5c45ad4a866ef))
* **deps:** update trpc monorepo to ^11.1.2 ([#2982](https://github.com/homarr-labs/homarr/issues/2982)) ([b10f33b](https://github.com/homarr-labs/homarr/commit/b10f33bf0b976217b2076ce84d8d1d9219b36e4c))
* **media-request:** add missing completed status ([#2990](https://github.com/homarr-labs/homarr/issues/2990)) ([9006d3d](https://github.com/homarr-labs/homarr/commit/9006d3d2fc21be5ba2eae616e467113356c91ebe))
* **releases-widget:** improve ui for smaller widgets ([#2962](https://github.com/homarr-labs/homarr/issues/2962)) ([f39a21c](https://github.com/homarr-labs/homarr/commit/f39a21c2962530298274ee705f3d85da023ea3e0))
* **unifi:** port is ignored ([#2995](https://github.com/homarr-labs/homarr/issues/2995)) ([0c9c5b8](https://github.com/homarr-labs/homarr/commit/0c9c5b8955c883191e5b6b137660c1a3cbd16ce4))
## [1.18.1](https://github.com/homarr-labs/homarr/compare/v1.18.0...v1.18.1) (2025-04-27)
### Bug Fixes
* cannot find module http-cookie-agent/http ([#2972](https://github.com/homarr-labs/homarr/issues/2972)) ([bb92f5b](https://github.com/homarr-labs/homarr/commit/bb92f5bb79e8f236dd149e87d14c1a35955140c7))
* **deps:** update dependency mysql2 to v3.14.1 ([#2969](https://github.com/homarr-labs/homarr/issues/2969)) ([5f80beb](https://github.com/homarr-labs/homarr/commit/5f80bebef2bd1598ad02dd1e169a1a6c27a4c143))
## [1.18.0](https://github.com/homarr-labs/homarr/compare/v1.17.0...v1.18.0) (2025-04-25)
### Features
* add releases widget ([#2497](https://github.com/homarr-labs/homarr/issues/2497)) ([3dcee8c](https://github.com/homarr-labs/homarr/commit/3dcee8cb869dd11d54631636253ad17e2af97dd7))
* **media-server:** add option to only show playing sessions ([#2899](https://github.com/homarr-labs/homarr/issues/2899)) ([f98750d](https://github.com/homarr-labs/homarr/commit/f98750d0b3e96b234d6f57b82bf7aa8729a091f2))
* migrate to unifi package ([#2894](https://github.com/homarr-labs/homarr/issues/2894)) ([8550c69](https://github.com/homarr-labs/homarr/commit/8550c69d51497676b654e204a85cdc84e8a38e30))
* **widgets:** add title to advanced options ([#2909](https://github.com/homarr-labs/homarr/issues/2909)) ([c64d903](https://github.com/homarr-labs/homarr/commit/c64d903f2b6375970325fb5031afd468dbf0ae11))
### Bug Fixes
* **certificates:** improve validation and prevent crash ([#2910](https://github.com/homarr-labs/homarr/issues/2910)) ([c514247](https://github.com/homarr-labs/homarr/commit/c51424717ddce5a2f4110658a1fbfab375185f66))
* **deps:** update dependency @auth/core to ^0.39.0 ([#2904](https://github.com/homarr-labs/homarr/issues/2904)) ([b0d7f9a](https://github.com/homarr-labs/homarr/commit/b0d7f9a48c05d380ff84ed7f48c9a9fd8dc33602))
* **deps:** update dependency @auth/drizzle-adapter to ^1.9.0 ([#2905](https://github.com/homarr-labs/homarr/issues/2905)) ([fdf700d](https://github.com/homarr-labs/homarr/commit/fdf700dba51d0bc4e72cdd63a63acb99c2196329))
* **deps:** update dependency @ctrl/qbittorrent to ^9.6.0 ([#2914](https://github.com/homarr-labs/homarr/issues/2914)) ([3d50808](https://github.com/homarr-labs/homarr/commit/3d508088d5a7d7fda53f49c196c2c6dd84d598d1))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.0 ([#2935](https://github.com/homarr-labs/homarr/issues/2935)) ([eb95ee2](https://github.com/homarr-labs/homarr/commit/eb95ee285b727a8bda7ac9df3f75c25ced475358))
* **deps:** update dependency dockerode to ^4.0.6 ([#2901](https://github.com/homarr-labs/homarr/issues/2901)) ([593e3ee](https://github.com/homarr-labs/homarr/commit/593e3ee5a6f9cb8d50a51262c85c3c067fce0691))
* **deps:** update dependency drizzle-orm to ^0.43.0 ([#2939](https://github.com/homarr-labs/homarr/issues/2939)) ([295cc4a](https://github.com/homarr-labs/homarr/commit/295cc4a3c41ba9ad20e93df9d6ea5627d56d310d))
* **deps:** update dependency drizzle-orm to ^0.43.1 ([#2947](https://github.com/homarr-labs/homarr/issues/2947)) ([1d28bf4](https://github.com/homarr-labs/homarr/commit/1d28bf4c93c43c1956c5b386ca8a02ca6ae59eb5))
* **deps:** update dependency glob to ^11.0.2 ([#2931](https://github.com/homarr-labs/homarr/issues/2931)) ([573d5f8](https://github.com/homarr-labs/homarr/commit/573d5f89d6576020c3a4393372a56f8f75628e58))
* **deps:** update dependency next-auth to v5.0.0-beta.26 ([#2906](https://github.com/homarr-labs/homarr/issues/2906)) ([6e6edc8](https://github.com/homarr-labs/homarr/commit/6e6edc8a0d809614588967b51751257929a6ccd3))
* **deps:** update dependency next-auth to v5.0.0-beta.27 ([#2940](https://github.com/homarr-labs/homarr/issues/2940)) ([2533083](https://github.com/homarr-labs/homarr/commit/253308357a55ab2fac7ac4144215beefdc517b8d))
* **deps:** update dependency next-intl to v4.0.3 ([#2922](https://github.com/homarr-labs/homarr/issues/2922)) ([3172e6e](https://github.com/homarr-labs/homarr/commit/3172e6e0c4e55f62a7ee32ca84fcaa940e774565))
* **deps:** update dependency next-intl to v4.1.0 ([#2938](https://github.com/homarr-labs/homarr/issues/2938)) ([767fe94](https://github.com/homarr-labs/homarr/commit/767fe946d78268eddce6e213665176b8c6397e92))
* **deps:** update dependency sass to ^1.87.0 ([#2921](https://github.com/homarr-labs/homarr/issues/2921)) ([fed28bd](https://github.com/homarr-labs/homarr/commit/fed28bd77546be83b9965bd1116996b621f6b639))
* **deps:** update dependency trpc-to-openapi to ^2.2.0 ([#2903](https://github.com/homarr-labs/homarr/issues/2903)) ([2bf67c3](https://github.com/homarr-labs/homarr/commit/2bf67c3de8609eb504e322d830f507973a55d6fc))
* **deps:** update dependency tsdav to ^2.1.4 ([#2944](https://github.com/homarr-labs/homarr/issues/2944)) ([d78b2e1](https://github.com/homarr-labs/homarr/commit/d78b2e1e85a6c72ce1366e50ee4a1e83176b7913))
* **deps:** update dependency typescript-eslint to ^8.31.0 ([#2919](https://github.com/homarr-labs/homarr/issues/2919)) ([7aa3790](https://github.com/homarr-labs/homarr/commit/7aa379076da24c90238dd5fe2714dbceb3b84c2a))
* **deps:** update mantine monorepo to ^7.17.5 ([#2937](https://github.com/homarr-labs/homarr/issues/2937)) ([aba963c](https://github.com/homarr-labs/homarr/commit/aba963c7cf0d554d51e5fc233e546e4917ecdf67))
* **deps:** update trpc monorepo to ^11.1.1 ([#2927](https://github.com/homarr-labs/homarr/issues/2927)) ([bd3535d](https://github.com/homarr-labs/homarr/commit/bd3535dcf84569b87cf647bc5e0b957667b7e199))
* **deps:** update turbo monorepo to ^2.5.1 ([#2934](https://github.com/homarr-labs/homarr/issues/2934)) ([7766c3c](https://github.com/homarr-labs/homarr/commit/7766c3cbbc1187f6c38157fe15182c8b7d329acf))
* **deps:** update turbo monorepo to ^2.5.2 ([#2949](https://github.com/homarr-labs/homarr/issues/2949)) ([d97e740](https://github.com/homarr-labs/homarr/commit/d97e74047dc107abb05ca65d8a91a41c6071fbd4))
## [1.17.0](https://github.com/homarr-labs/homarr/compare/v1.16.0...v1.17.0) (2025-04-18)
### Features
* add api-key support for tdarr ([#2890](https://github.com/homarr-labs/homarr/issues/2890)) ([3d68f4d](https://github.com/homarr-labs/homarr/commit/3d68f4d1283b6cb27f9532d1adf52cef78a0fb03))
* Prefer primary color ([#2665](https://github.com/homarr-labs/homarr/issues/2665)) ([00053bd](https://github.com/homarr-labs/homarr/commit/00053bda7bbcaaf359074c3b35573ee0afc1409d))
### Bug Fixes
* calculate available space correctly and filter unmounted drives ([#2885](https://github.com/homarr-labs/homarr/issues/2885)) ([cfca0ba](https://github.com/homarr-labs/homarr/commit/cfca0ba58323ec09f8f64da2fee251ef14ba914a))
* **deps:** update dependency drizzle-kit to ^0.31.0 ([#2881](https://github.com/homarr-labs/homarr/issues/2881)) ([97390c6](https://github.com/homarr-labs/homarr/commit/97390c60300f3d16dabf0710fa435c6b3ae86a6f))
* **deps:** update dependency drizzle-orm to ^0.42.0 ([#2882](https://github.com/homarr-labs/homarr/issues/2882)) ([47d7abc](https://github.com/homarr-labs/homarr/commit/47d7abc0d739b91328834e0eaea033637632f22c))
* **deps:** update dependency jotai to ^2.12.3 ([#2868](https://github.com/homarr-labs/homarr/issues/2868)) ([c57b63b](https://github.com/homarr-labs/homarr/commit/c57b63b506733459ea0c09776917042fa878829b))
* **deps:** update dependency recharts to ^2.15.3 ([#2893](https://github.com/homarr-labs/homarr/issues/2893)) ([5fcb7a3](https://github.com/homarr-labs/homarr/commit/5fcb7a3b9c279c1b402e120a4a6f08265e8d5912))
* **deps:** update dependency swagger-ui-react to ^5.21.0 ([#2866](https://github.com/homarr-labs/homarr/issues/2866)) ([ff1263b](https://github.com/homarr-labs/homarr/commit/ff1263b217e96cbb2bc6b8e98ff117c44ca9f00d))
* **deps:** update dependency typescript-eslint to ^8.30.0 ([#2873](https://github.com/homarr-labs/homarr/issues/2873)) ([089734b](https://github.com/homarr-labs/homarr/commit/089734b293a21d6eeaa7c5df5ee9930853ed2fcf))
* **deps:** update dependency typescript-eslint to ^8.30.1 ([#2876](https://github.com/homarr-labs/homarr/issues/2876)) ([fdca513](https://github.com/homarr-labs/homarr/commit/fdca513929e9e594af2d59ad17c915cbc7ac2e08))
* **deps:** update dependency zod to ^3.24.3 ([#2886](https://github.com/homarr-labs/homarr/issues/2886)) ([1bfe397](https://github.com/homarr-labs/homarr/commit/1bfe397d68a88037e35701aab8a7b4c9027ec1e3))
* **deps:** update nextjs monorepo to v15.3.1 ([#2889](https://github.com/homarr-labs/homarr/issues/2889)) ([d37cdbd](https://github.com/homarr-labs/homarr/commit/d37cdbd080c2b3a4df536ab7dad4fc4a4b3c915f))
* **deps:** update tanstack-query monorepo to ^5.73.3 ([#2860](https://github.com/homarr-labs/homarr/issues/2860)) ([4e6ab03](https://github.com/homarr-labs/homarr/commit/4e6ab037c9245103b05afe977e9c288082a2b539))
* **deps:** update tanstack-query monorepo to ^5.74.0 ([#2864](https://github.com/homarr-labs/homarr/issues/2864)) ([ac78e87](https://github.com/homarr-labs/homarr/commit/ac78e8704287f45d6a8ca26cba9b87146e1c6161))
* **deps:** update tanstack-query monorepo to ^5.74.2 ([#2869](https://github.com/homarr-labs/homarr/issues/2869)) ([bf4a1cf](https://github.com/homarr-labs/homarr/commit/bf4a1cf1e8c99ada97adf6ee118ddaafc00115e9))
* **deps:** update tanstack-query monorepo to ^5.74.3 ([#2871](https://github.com/homarr-labs/homarr/issues/2871)) ([02fb1b9](https://github.com/homarr-labs/homarr/commit/02fb1b9dae501e475353e8e2510e4f8938c4e124))
* **deps:** update tanstack-query monorepo to ^5.74.4 ([#2888](https://github.com/homarr-labs/homarr/issues/2888)) ([17174b6](https://github.com/homarr-labs/homarr/commit/17174b6b96ae16b480e2bc5eb9f8ab7b914bc843))
* Tdarr widget empty statistics tab ([#2883](https://github.com/homarr-labs/homarr/issues/2883)) ([2d12c1e](https://github.com/homarr-labs/homarr/commit/2d12c1e641398e20a70f8bfb24a48de02498496a))
* **widget:** format stock values and changes for better readability ([#2836](https://github.com/homarr-labs/homarr/issues/2836)) ([a29556e](https://github.com/homarr-labs/homarr/commit/a29556ee8ec50aa384a9a46f6f5a5fa5dfa4cdac))
## [1.16.0](https://github.com/homarr-labs/homarr/compare/v1.15.0...v1.16.0) (2025-04-11)
### Features
* **boards:** add quick app add menu item ([#2681](https://github.com/homarr-labs/homarr/issues/2681)) ([7a3c836](https://github.com/homarr-labs/homarr/commit/7a3c836a70a9b9656c6fe74fe6a4bf8e7166a030))
* support aria2 integration ([#2226](https://github.com/homarr-labs/homarr/issues/2226)) ([94263c4](https://github.com/homarr-labs/homarr/commit/94263c445b8a0d0cdbea70ebe45ef932b426204d))
### Bug Fixes
* **boards:** add missing upload button for background in settings ([#2853](https://github.com/homarr-labs/homarr/issues/2853)) ([4baec7e](https://github.com/homarr-labs/homarr/commit/4baec7e3ff33ac0973daf0ac1f0171e349787bdb))
* **deps:** update dependency dotenv to ^16.5.0 ([#2848](https://github.com/homarr-labs/homarr/issues/2848)) ([c84dd2f](https://github.com/homarr-labs/homarr/commit/c84dd2fd797ca674e4f553ddc71926e9b533daa4))
* **deps:** update dependency eslint-config-prettier to ^10.1.2 ([#2842](https://github.com/homarr-labs/homarr/issues/2842)) ([924a7d0](https://github.com/homarr-labs/homarr/commit/924a7d076ef8bdd67afcce2e9f850bf334b43c3a))
* **deps:** update dependency ioredis to v5.6.1 ([#2851](https://github.com/homarr-labs/homarr/issues/2851)) ([0ed6a79](https://github.com/homarr-labs/homarr/commit/0ed6a79a5bba25fa7a90bec00d3815b3fffae427))
* **deps:** update dependency octokit to ^4.1.3 ([#2847](https://github.com/homarr-labs/homarr/issues/2847)) ([4b0b892](https://github.com/homarr-labs/homarr/commit/4b0b89225000137f02f3b89376c46d4d01387128))
* **deps:** update dependency swagger-ui-react to ^5.20.8 ([#2844](https://github.com/homarr-labs/homarr/issues/2844)) ([45d285a](https://github.com/homarr-labs/homarr/commit/45d285a76ba46d4d4c594f1f2e822d784a281b72))
* **deps:** update dependency undici to v7.8.0 ([#2841](https://github.com/homarr-labs/homarr/issues/2841)) ([f13be4a](https://github.com/homarr-labs/homarr/commit/f13be4a70870aca99a7f49f5068dadcd7fe01c22))
* **deps:** update nextjs monorepo to v15.3.0 ([#2838](https://github.com/homarr-labs/homarr/issues/2838)) ([a1fa429](https://github.com/homarr-labs/homarr/commit/a1fa4297ead54a9f8c842b2d23001f725359e000))
* **deps:** update tanstack-query monorepo to ^5.72.2 ([#2834](https://github.com/homarr-labs/homarr/issues/2834)) ([f5e3138](https://github.com/homarr-labs/homarr/commit/f5e3138c8118295d8230c3655af1c3f11ee5d4ff))
* **deps:** update testcontainers-node monorepo to ^10.24.2 ([#2846](https://github.com/homarr-labs/homarr/issues/2846)) ([057ff2c](https://github.com/homarr-labs/homarr/commit/057ff2cfc1cf062a3c48deb28699df8e1ba89f75))
* **deps:** update trpc monorepo to ^11.1.0 ([#2850](https://github.com/homarr-labs/homarr/issues/2850)) ([0fe4ea9](https://github.com/homarr-labs/homarr/commit/0fe4ea9dd9cbfb2def1bede9db8eaf649f79f1b0))
## [1.15.0](https://github.com/homarr-labs/homarr/compare/v1.14.0...v1.15.0) (2025-04-09)
### Features
* **boards:** add upload buttons for background, favicon and logo ([#2770](https://github.com/homarr-labs/homarr/issues/2770)) ([e07b9e6](https://github.com/homarr-labs/homarr/commit/e07b9e6a88ff0aad0aa5dae2f32f648ad5f0c0a8))
* unifi controller integration ([#2236](https://github.com/homarr-labs/homarr/issues/2236)) ([c1cd563](https://github.com/homarr-labs/homarr/commit/c1cd56304872b5f7b2b49624c6f5018f750d126b))
### Bug Fixes
* **deps:** update dependency @kubernetes/client-node to ^1.1.2 ([#2825](https://github.com/homarr-labs/homarr/issues/2825)) ([b064304](https://github.com/homarr-labs/homarr/commit/b064304201ed2ce52162edd5bc9ab5131da7ac97))
* **deps:** update dependency ldapts to v7.4.0 ([#2815](https://github.com/homarr-labs/homarr/issues/2815)) ([275224e](https://github.com/homarr-labs/homarr/commit/275224ee68bb49f329a6765095b46ec249027aca))
* **deps:** update dependency swagger-ui-react to ^5.20.7 ([#2816](https://github.com/homarr-labs/homarr/issues/2816)) ([85f8d23](https://github.com/homarr-labs/homarr/commit/85f8d23da3f3ec17945d79ef8a5212c5ceeefe75))
* **deps:** update dependency typescript-eslint to ^8.29.1 ([#2821](https://github.com/homarr-labs/homarr/issues/2821)) ([17d9272](https://github.com/homarr-labs/homarr/commit/17d92727f24cf0e2491dc93fd7c72759a24bbd54))
* **deps:** update mantine monorepo to ^7.17.4 ([#2824](https://github.com/homarr-labs/homarr/issues/2824)) ([bd5dcb9](https://github.com/homarr-labs/homarr/commit/bd5dcb991c8bd476cefbae1b8b2f407dd8db1acb))
* **deps:** update nextjs monorepo to v15.2.5 ([#2828](https://github.com/homarr-labs/homarr/issues/2828)) ([0fc7ca7](https://github.com/homarr-labs/homarr/commit/0fc7ca7738aee2d86940f43e3593368d4102cc73))
* **deps:** update tanstack-query monorepo to ^5.72.0 ([#2817](https://github.com/homarr-labs/homarr/issues/2817)) ([df212cf](https://github.com/homarr-labs/homarr/commit/df212cf5114a970ad4aed84811cfc7a2e4be67b3))
* **deps:** update tanstack-query monorepo to ^5.72.1 ([#2823](https://github.com/homarr-labs/homarr/issues/2823)) ([c01d9a4](https://github.com/homarr-labs/homarr/commit/c01d9a4dc5aba540a2b333a94507d027e5fb8d99))
* **deps:** update testcontainers-node monorepo to ^10.24.1 ([#2831](https://github.com/homarr-labs/homarr/issues/2831)) ([092c95b](https://github.com/homarr-labs/homarr/commit/092c95bbe0be73ad3dedb13c7f84ba7c36ca14db))
* **deps:** update trpc monorepo to ^11.0.3 ([#2822](https://github.com/homarr-labs/homarr/issues/2822)) ([a821bd7](https://github.com/homarr-labs/homarr/commit/a821bd71f47e03a276a96af705bec90a00229e0f))
* **deps:** update trpc monorepo to ^11.0.4 ([#2827](https://github.com/homarr-labs/homarr/issues/2827)) ([75100d1](https://github.com/homarr-labs/homarr/commit/75100d1a4926b838682206189eac046d844dba13))
* **medias:** upload size limited by nginx to 1mb ([#2813](https://github.com/homarr-labs/homarr/issues/2813)) ([bf046a7](https://github.com/homarr-labs/homarr/commit/bf046a715cd6897792b2f1582d076c055063bf57))
* **performance:** remove turbopack flag to no longer go out of memory after ([#2803](https://github.com/homarr-labs/homarr/issues/2803)) ([c6102ed](https://github.com/homarr-labs/homarr/commit/c6102edff467082803769c488ce642500edcdd25))
* **security:** missing authorization check for changes to notebook widget ([#2832](https://github.com/homarr-labs/homarr/issues/2832)) ([3948f7f](https://github.com/homarr-labs/homarr/commit/3948f7f9a47cd4562c9b4fd8252f116b8a95c982))
## [1.14.0](https://github.com/homarr-labs/homarr/compare/v1.13.1...v1.14.0) (2025-04-04)
### Features
* **cli:** add cli command to recreate credentials admin ([#2779](https://github.com/homarr-labs/homarr/issues/2779)) ([6742532](https://github.com/homarr-labs/homarr/commit/674253262bd4853ebde85d229d666a38733a7026))
### Bug Fixes
* **deps:** update dependency @ctrl/qbittorrent to ^9.5.2 ([#2795](https://github.com/homarr-labs/homarr/issues/2795)) ([ea8bf43](https://github.com/homarr-labs/homarr/commit/ea8bf43d7b56f526fbf2d4a3955c2c179e4e74d8))
* **deps:** update dependency @kubernetes/client-node to ^1.1.1 ([#2793](https://github.com/homarr-labs/homarr/issues/2793)) ([76b8b35](https://github.com/homarr-labs/homarr/commit/76b8b35cc1f586797210bdcd3d848d64b5f36d63))
* **deps:** update dependency dockerode to ^4.0.5 ([#2757](https://github.com/homarr-labs/homarr/issues/2757)) ([db13fd0](https://github.com/homarr-labs/homarr/commit/db13fd03c018cb2f79f0b094b93b69d69fde97ff))
* **deps:** update dependency eslint-plugin-react to ^7.37.5 ([#2794](https://github.com/homarr-labs/homarr/issues/2794)) ([f9aeb8f](https://github.com/homarr-labs/homarr/commit/f9aeb8ffe3ff4cd65cd76c560a687912262a735f))
* **deps:** update dependency recharts to ^2.15.2 ([#2785](https://github.com/homarr-labs/homarr/issues/2785)) ([f33b1cb](https://github.com/homarr-labs/homarr/commit/f33b1cb5d0c317df5348e87e0f5f43fdc6044615))
* **deps:** update dependency sass to ^1.86.1 ([#2762](https://github.com/homarr-labs/homarr/issues/2762)) ([3e35051](https://github.com/homarr-labs/homarr/commit/3e3505176a7df01bae5173d7f62380fa17859e8f))
* **deps:** update dependency sass to ^1.86.2 ([#2784](https://github.com/homarr-labs/homarr/issues/2784)) ([3131082](https://github.com/homarr-labs/homarr/commit/3131082c778191be5568a93bbf5f10219f3c2be9))
* **deps:** update dependency sass to ^1.86.3 ([#2796](https://github.com/homarr-labs/homarr/issues/2796)) ([98ecc68](https://github.com/homarr-labs/homarr/commit/98ecc683e563475644530682f08d335ac3671a5e))
* **deps:** update dependency swagger-ui-react to ^5.20.3 ([#2777](https://github.com/homarr-labs/homarr/issues/2777)) ([81d1231](https://github.com/homarr-labs/homarr/commit/81d1231d8fc026dd9f1ba58e28937aba9d94d88e))
* **deps:** update dependency swagger-ui-react to ^5.20.4 ([#2786](https://github.com/homarr-labs/homarr/issues/2786)) ([85672d9](https://github.com/homarr-labs/homarr/commit/85672d9cf15bf48c9cc2fb4e746f65814f46361b))
* **deps:** update dependency swagger-ui-react to ^5.20.5 ([#2787](https://github.com/homarr-labs/homarr/issues/2787)) ([3363eeb](https://github.com/homarr-labs/homarr/commit/3363eebdddf10a028d1cdebcb4d581105ba1d63c))
* **deps:** update dependency swagger-ui-react to ^5.20.6 ([#2798](https://github.com/homarr-labs/homarr/issues/2798)) ([968a82b](https://github.com/homarr-labs/homarr/commit/968a82baf5d6adc64c680508493b77b892fdd5fc))
* **deps:** update dependency trpc-to-openapi to ^2.1.4 ([#2748](https://github.com/homarr-labs/homarr/issues/2748)) ([c69e331](https://github.com/homarr-labs/homarr/commit/c69e331ded3cfde837dcf28c8b1ba4a9b7a3c8aa))
* **deps:** update dependency trpc-to-openapi to ^2.1.5 ([#2799](https://github.com/homarr-labs/homarr/issues/2799)) ([b03941e](https://github.com/homarr-labs/homarr/commit/b03941ed80057154f0b4b04c764652ada1fdf18c))
* **deps:** update dependency typescript-eslint to ^8.29.0 ([#2761](https://github.com/homarr-labs/homarr/issues/2761)) ([3687c4b](https://github.com/homarr-labs/homarr/commit/3687c4b3d655d9cebb85fb3c3ad80f494e9a8f0a))
* **deps:** update dependency undici to v7.7.0 ([#2776](https://github.com/homarr-labs/homarr/issues/2776)) ([ff5c313](https://github.com/homarr-labs/homarr/commit/ff5c31342fe9eb668533d7d9f0d65ad3f01eba0a))
* **deps:** update tanstack-query monorepo to ^5.71.0 ([#2752](https://github.com/homarr-labs/homarr/issues/2752)) ([77044e8](https://github.com/homarr-labs/homarr/commit/77044e8e0b8e7267b8547eb1d0f86026948b292a))
* **deps:** update tanstack-query monorepo to ^5.71.1 ([#2756](https://github.com/homarr-labs/homarr/issues/2756)) ([0b8a009](https://github.com/homarr-labs/homarr/commit/0b8a0098bd02a71107af9702e39000227028ec20))
* **deps:** update tanstack-query monorepo to ^5.71.10 ([#2790](https://github.com/homarr-labs/homarr/issues/2790)) ([964368a](https://github.com/homarr-labs/homarr/commit/964368a06c8385df63a252e2d0759a2524348ac6))
* **deps:** update tanstack-query monorepo to ^5.71.3 ([#2767](https://github.com/homarr-labs/homarr/issues/2767)) ([858fe6e](https://github.com/homarr-labs/homarr/commit/858fe6e19a682bb0fa0cfa0d11c2423447fdad2f))
* **deps:** update tanstack-query monorepo to ^5.71.5 ([#2780](https://github.com/homarr-labs/homarr/issues/2780)) ([466d0e0](https://github.com/homarr-labs/homarr/commit/466d0e00530903ab4101cd2e94c00ab8a7c1c8ce))
* **deps:** update testcontainers-node monorepo to ^10.24.0 ([#2789](https://github.com/homarr-labs/homarr/issues/2789)) ([1b05041](https://github.com/homarr-labs/homarr/commit/1b0504198b41032319ead2604721abaaf08709d8))
* **deps:** update tiptap monorepo to v2.11.7 ([#2749](https://github.com/homarr-labs/homarr/issues/2749)) ([0ec8f23](https://github.com/homarr-labs/homarr/commit/0ec8f23b7c655889d8f6fb9fcfd66a9736eb5a10))
* **deps:** update tiptap monorepo to v2.11.7 ([#2750](https://github.com/homarr-labs/homarr/issues/2750)) ([59b1630](https://github.com/homarr-labs/homarr/commit/59b16305326ac811ad91e89c0786b9e6b1b7e1df))
* **deps:** update trpc monorepo to ^11.0.2 ([#2792](https://github.com/homarr-labs/homarr/issues/2792)) ([8886380](https://github.com/homarr-labs/homarr/commit/8886380f0a9ea7d35cd54182a378b2f47b71c283))
* **deps:** update turbo monorepo to ^2.5.0 ([#2791](https://github.com/homarr-labs/homarr/issues/2791)) ([0b41967](https://github.com/homarr-labs/homarr/commit/0b41967ec366d925dbb172d38c3677d88f51f0a9))
* **nextcloud:** issue with self signed certificates ([#2760](https://github.com/homarr-labs/homarr/issues/2760)) ([7f77130](https://github.com/homarr-labs/homarr/commit/7f77130978eb7f25e571b7315250c91656a85d78))
* update check blocks page loading ([#2782](https://github.com/homarr-labs/homarr/issues/2782)) ([29ff879](https://github.com/homarr-labs/homarr/commit/29ff879dfd1036d44b7c4735a0b269a83173b4d1))
### Reverts
* add restriction callback to restrict visibility and modification of widget kinds ([#2746](https://github.com/homarr-labs/homarr/issues/2746)) ([9851531](https://github.com/homarr-labs/homarr/commit/98515312a2439d1ed9fc5b412843ca625120b2ac))
## [1.13.1](https://github.com/homarr-labs/homarr/compare/v1.13.0...v1.13.1) (2025-03-28)
### Bug Fixes
* **deps:** update react monorepo to v19.1.0 ([#2734](https://github.com/homarr-labs/homarr/issues/2734)) ([94ff401](https://github.com/homarr-labs/homarr/commit/94ff401d7dface924f48affccbc59e3a98ed8dc1))
### Reverts
* ssg integration create ([#2736](https://github.com/homarr-labs/homarr/issues/2736)) ([fef50a5](https://github.com/homarr-labs/homarr/commit/fef50a53b1fb592668dfa9aade6b110e42aec9a8))
## [1.13.0](https://github.com/homarr-labs/homarr/compare/v1.12.0...v1.13.0) (2025-03-28)
### Features
* add default search engines seeding / set homarr docs as global server search engine ([#2663](https://github.com/homarr-labs/homarr/issues/2663)) ([3e1c000](https://github.com/homarr-labs/homarr/commit/3e1c000d515c0edc6f49241fc381187e1829ca7e))
* add german swiss locale ([#2726](https://github.com/homarr-labs/homarr/issues/2726)) ([eba738e](https://github.com/homarr-labs/homarr/commit/eba738e5d6594d46b5b7d1eab875fc8060e0fe7b))
* add jetbrains run profile ([#2704](https://github.com/homarr-labs/homarr/issues/2704)) ([07f0abf](https://github.com/homarr-labs/homarr/commit/07f0abf28cb31f81860e623c70970e16d17e2836))
* add title to dynamic section ([#2614](https://github.com/homarr-labs/homarr/issues/2614)) ([1dd7b1a](https://github.com/homarr-labs/homarr/commit/1dd7b1a6b97f75bca0d1a5c0dbb4254aa4bbd418))
* app dropdown add infinity scroll ([#2696](https://github.com/homarr-labs/homarr/issues/2696)) ([8bf893b](https://github.com/homarr-labs/homarr/commit/8bf893b39292f34f5c697c09ba2c9e53c8a6ea90))
* **auth:** add env variable to force user-info usage instead of idtoken ([#2711](https://github.com/homarr-labs/homarr/issues/2711)) ([94d3dad](https://github.com/homarr-labs/homarr/commit/94d3dad909c39cec096ff61c0a2f45e8853b6111))
* auto sign-in when creating the initial user ([#2664](https://github.com/homarr-labs/homarr/issues/2664)) ([46c95e0](https://github.com/homarr-labs/homarr/commit/46c95e0da3ad27eb072efc45250abb0baf3e2483))
* implement auto-select icon ([#2679](https://github.com/homarr-labs/homarr/issues/2679)) ([1a3a559](https://github.com/homarr-labs/homarr/commit/1a3a55934d8b3e4703e0ce44d9d26ad7ff100aca))
* **lang:** add english uk language ([#2715](https://github.com/homarr-labs/homarr/issues/2715)) ([93c64ba](https://github.com/homarr-labs/homarr/commit/93c64baec294c39cd490a85f8b06b57d53fc82b3))
* **logs:** improve logs by logging errors with causes and metadata ([#2703](https://github.com/homarr-labs/homarr/issues/2703)) ([579dd57](https://github.com/homarr-labs/homarr/commit/579dd5763d139e1644c2e192620728afa54c96d2))
* set home board when creating first board ([#2683](https://github.com/homarr-labs/homarr/issues/2683)) ([8f7f211](https://github.com/homarr-labs/homarr/commit/8f7f2110ec29db12ea2d83cd779ec86851209188))
* SSG integration creation page ([#2684](https://github.com/homarr-labs/homarr/issues/2684)) ([ce0705e](https://github.com/homarr-labs/homarr/commit/ce0705e9d4a502273a0d84e4e14d61a061fd21f9))
* **widget:** add restriction callback to restrict visibility and modification of widget kinds ([#2658](https://github.com/homarr-labs/homarr/issues/2658)) ([84f73d3](https://github.com/homarr-labs/homarr/commit/84f73d33a061a1d243803078b245a8016a615af9))
### Bug Fixes
* allow deletion of external users ([#2725](https://github.com/homarr-labs/homarr/issues/2725)) ([40e0098](https://github.com/homarr-labs/homarr/commit/40e00987cb6f4ee58fdce1da9489eedd92b8843d))
* **auth:** fusion auth returns www-authenticate token response ([#2719](https://github.com/homarr-labs/homarr/issues/2719)) ([b5b0b06](https://github.com/homarr-labs/homarr/commit/b5b0b069d2029110d5618a29af5ae003030584d9))
* bookmarks layout ([#2675](https://github.com/homarr-labs/homarr/issues/2675)) ([8a071f9](https://github.com/homarr-labs/homarr/commit/8a071f9ab6bc4c85073dc52cc2e60da69f30950a))
* calendar indicators ([#2712](https://github.com/homarr-labs/homarr/issues/2712)) ([70ea093](https://github.com/homarr-labs/homarr/commit/70ea093eb6530d25d3e60e6d496651e3fc457826))
* **deps:** update dependency drizzle-kit to ^0.30.6 ([#2706](https://github.com/homarr-labs/homarr/issues/2706)) ([f03e381](https://github.com/homarr-labs/homarr/commit/f03e381aea414c5fb770c7ba092b31f9c53eee11))
* **deps:** update dependency drizzle-zod to ^0.7.1 ([#2717](https://github.com/homarr-labs/homarr/issues/2717)) ([569ec6f](https://github.com/homarr-labs/homarr/commit/569ec6fe5ccec980351162024170fabe06dcc3da))
* **deps:** update dependency ldapts to v7.3.3 ([#2691](https://github.com/homarr-labs/homarr/issues/2691)) ([00f5a87](https://github.com/homarr-labs/homarr/commit/00f5a877a6f09037eff4c9934fc273c4d2af0f6c))
* **deps:** update dependency swagger-ui-react to ^5.20.2 ([#2708](https://github.com/homarr-labs/homarr/issues/2708)) ([68407ea](https://github.com/homarr-labs/homarr/commit/68407eaa195894db37018cefeeca4ae2da085847))
* **deps:** update dependency typescript-eslint to ^8.28.0 ([#2693](https://github.com/homarr-labs/homarr/issues/2693)) ([f624611](https://github.com/homarr-labs/homarr/commit/f6246115405cdc85948059170742b0bae58ab337))
* **deps:** update dependency undici to v7.6.0 ([#2710](https://github.com/homarr-labs/homarr/issues/2710)) ([43b8cfa](https://github.com/homarr-labs/homarr/commit/43b8cfa4dd250013fdf15d509a072ba0984f9ab1))
* **deps:** update mantine monorepo to ^7.17.3 ([#2707](https://github.com/homarr-labs/homarr/issues/2707)) ([6854e47](https://github.com/homarr-labs/homarr/commit/6854e4729d3ba4a82a318813fec0a20eccdbed18))
* **deps:** update nextjs monorepo to v15.2.4 ([#2720](https://github.com/homarr-labs/homarr/issues/2720)) ([ac77bdd](https://github.com/homarr-labs/homarr/commit/ac77bdd84cba47a43d1952725a9c7feccd972766))
* **deps:** update tanstack-query monorepo to ^5.69.2 ([#2714](https://github.com/homarr-labs/homarr/issues/2714)) ([78b5520](https://github.com/homarr-labs/homarr/commit/78b55202e7f4739933768d27211556f927b04fed))
* **deps:** update tanstack-query monorepo to ^5.69.3 ([#2716](https://github.com/homarr-labs/homarr/issues/2716)) ([e7e80db](https://github.com/homarr-labs/homarr/commit/e7e80dbc7d62830ce1ce150b6e22a67220da8be8))
* **deps:** update tanstack-query monorepo to ^5.70.0 ([#2722](https://github.com/homarr-labs/homarr/issues/2722)) ([34eb9b3](https://github.com/homarr-labs/homarr/commit/34eb9b366fdc58c9fc03d18ac7dd68bff1ea83e6))
* **deps:** update testcontainers-node monorepo to ^10.22.0 ([#2692](https://github.com/homarr-labs/homarr/issues/2692)) ([b7a6d95](https://github.com/homarr-labs/homarr/commit/b7a6d950db557ea9deadc0be014b5f218611ac83))
* **deps:** update testcontainers-node monorepo to ^10.23.0 ([#2699](https://github.com/homarr-labs/homarr/issues/2699)) ([c3264b2](https://github.com/homarr-labs/homarr/commit/c3264b2fe10ad374e92eb16921d43427d7ca866b))
* **deps:** update tiptap monorepo to v2.11.6 ([#2718](https://github.com/homarr-labs/homarr/issues/2718)) ([0ed4c74](https://github.com/homarr-labs/homarr/commit/0ed4c741c59c3e498aa247adc292bdbbde1c59d3))
* **deps:** update trpc monorepo to ^11.0.1 ([#2724](https://github.com/homarr-labs/homarr/issues/2724)) ([2178185](https://github.com/homarr-labs/homarr/commit/217818573e8bd7d49a821afdf269cd8b3f3d442d))
* **import:** autofix missing shapes for sidebars and some sections as well ([#2723](https://github.com/homarr-labs/homarr/issues/2723)) ([62c7955](https://github.com/homarr-labs/homarr/commit/62c7955e48ca62b4bf1baf26ac6a12847795ac18))
* Media request via search engine ([#2677](https://github.com/homarr-labs/homarr/issues/2677)) ([5a8405a](https://github.com/homarr-labs/homarr/commit/5a8405a9a81257b023c0265309f7f530b2e23c7a))
### Reverts
* add development package and update workspace ([#2700](https://github.com/homarr-labs/homarr/issues/2700))" ([#2728](https://github.com/homarr-labs/homarr/issues/2728)) ([996ec54](https://github.com/homarr-labs/homarr/commit/996ec548c4b9c8561919409b81adc90b0a24fbd6))
## [1.12.0](https://github.com/homarr-labs/homarr/compare/v1.11.0...v1.12.0) (2025-03-21)
### Features
+5 -3
View File
@@ -1,4 +1,4 @@
FROM node:22.18.0-alpine AS base
FROM node:22.14.0-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
@@ -10,6 +10,8 @@ RUN apk add --no-cache libc6-compat curl bash
RUN apk update
COPY . .
# Install working version of corepack (See https://github.com/nodejs/corepack/issues/612)
RUN npm install -g corepack@0.31.0 && corepack --version
RUN corepack enable pnpm && pnpm install --recursive --frozen-lockfile
# Copy static data as it is not part of the build
@@ -17,7 +19,8 @@ COPY static-data ./static-data
ARG SKIP_ENV_VALIDATION='true'
ARG CI='true'
ARG DISABLE_REDIS_LOGS='true'
# Install working version of corepack (See https://github.com/nodejs/corepack/issues/612)
RUN npm install -g corepack@0.31.0 && corepack --version
RUN corepack enable pnpm && pnpm build
FROM base AS runner
@@ -64,7 +67,6 @@ ENV DB_URL='/appdata/db/db.sqlite'
ENV DB_DIALECT='sqlite'
ENV DB_DRIVER='better-sqlite3'
ENV AUTH_PROVIDERS='credentials'
ENV REDIS_IS_EXTERNAL='false'
ENV NODE_ENV='production'
ENTRYPOINT [ "/app/entrypoint.sh" ]
+4 -4
View File
@@ -1,18 +1,18 @@
# Security Policy
This policy is relevant if you found potential vulnerabilities in an audit.
We consider something as a vulnerability if it...
1. puts users or user data at risk
2. enables third parties to gain control or access (e.g. [RATs](https://en.wikipedia.org/wiki/Remote_desktop_software#RAT), [privilege escalation](https://en.wikipedia.org/wiki/Privilege_escalation), ...)
3. abuses the system in an unintended way (e.g. crypto mining, proxy, ...)
## Supported Versions
We only fix security issues in the [latest stable version](https://github.com/homarr-labs/homarr/releases/latest). Meaning security issues in prior versions will not be fixed and users have to upgrade to the latest version to receive them.
| Version | Supported |
| ------- | ------------------ |
| >1.0.0 | :white_check_mark: |
| <1.0.0 | :x: |
## Reporting a Vulnerability
We use [GitHub's system for reporting vulnerabilities](https://docs.github.com/en/enterprise-cloud@latest/code-security/security-advisories/working-with-repository-security-advisories/creating-a-repository-security-advisory).
Click [**here to report an advisory**](https://github.com/homarr-labs/homarr/security/advisories/new). Our team will get notified and will get back to you within 1-6 business days.
+19 -32
View File
@@ -17,16 +17,31 @@ const withNextIntl = createNextIntlPlugin({
requestConfig: "../../packages/translation/src/request.ts",
});
interface WebpackConfig {
module: {
rules: {
test: RegExp;
loader: string;
}[];
};
}
const nextConfig: NextConfig = {
output: "standalone",
reactStrictMode: true,
/** We already do linting and typechecking as separate tasks in CI */
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
/**
* dockerode is required in the external server packages because of https://github.com/homarr-labs/homarr/issues/612
*/
serverExternalPackages: ["dockerode"],
webpack: (config: WebpackConfig, { isServer }) => {
if (isServer) {
config.module.rules.push({
test: /\.node$/,
loader: "node-loader",
});
}
return config;
},
experimental: {
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
},
@@ -34,34 +49,6 @@ const nextConfig: NextConfig = {
images: {
domains: ["cdn.jsdelivr.net"],
},
// eslint-disable-next-line @typescript-eslint/require-await,no-restricted-syntax
async headers() {
return [
{
source: "/(.*)", // Apply CSP to all routes
headers: [
{
key: "Content-Security-Policy",
value: `
default-src 'self';
script-src * 'unsafe-inline' 'unsafe-eval';
base-uri 'self';
connect-src *;
style-src * 'unsafe-inline';
frame-ancestors *;
frame-src *;
form-action 'self';
img-src * data:;
font-src * data:;
media-src * data:;
`
.replace(/\s{2,}/g, " ")
.trim(),
},
],
},
];
},
};
// Skip transform is used because of webpack loader, without it for example 'Tooltip.Floating' will not work and show an error
+34 -36
View File
@@ -6,7 +6,7 @@
"scripts": {
"build": "pnpm with-env next build",
"clean": "git clean -xdf .next .turbo node_modules",
"dev": "pnpm with-env next dev",
"dev": "pnpm with-env next dev --turbopack",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"start": "pnpm with-env next start",
@@ -25,7 +25,6 @@
"@homarr/boards": "workspace:^0.1.0",
"@homarr/certificates": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/cron-job-status": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
@@ -34,7 +33,6 @@
"@homarr/forms-collection": "workspace:^0.1.0",
"@homarr/gridstack": "^1.12.0",
"@homarr/icons": "workspace:^0.1.0",
"@homarr/image-proxy": "workspace:^0.1.0",
"@homarr/integrations": "workspace:^0.1.0",
"@homarr/log": "workspace:^",
"@homarr/modals": "workspace:^0.1.0",
@@ -50,59 +48,59 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"@mantine/colors-generator": "^8.2.5",
"@mantine/core": "^8.2.5",
"@mantine/dropzone": "^8.2.5",
"@mantine/hooks": "^8.2.5",
"@mantine/modals": "^8.2.5",
"@mantine/tiptap": "^8.2.5",
"@mantine/colors-generator": "^7.17.3",
"@mantine/core": "^7.17.3",
"@mantine/dropzone": "^7.17.3",
"@mantine/hooks": "^7.17.3",
"@mantine/modals": "^7.17.3",
"@mantine/tiptap": "^7.17.3",
"@million/lint": "1.0.14",
"@tabler/icons-react": "^3.34.1",
"@tanstack/react-query": "^5.85.5",
"@tanstack/react-query-devtools": "^5.85.5",
"@tanstack/react-query-next-experimental": "^5.85.5",
"@trpc/client": "^11.4.4",
"@trpc/next": "^11.4.4",
"@trpc/react-query": "^11.4.4",
"@trpc/server": "^11.4.4",
"@tabler/icons-react": "^3.31.0",
"@tanstack/react-query": "^5.70.0",
"@tanstack/react-query-devtools": "^5.70.0",
"@tanstack/react-query-next-experimental": "^5.70.0",
"@trpc/client": "^11.0.1",
"@trpc/next": "^11.0.1",
"@trpc/react-query": "^11.0.1",
"@trpc/server": "^11.0.1",
"@xterm/addon-canvas": "^0.7.0",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "^5.5.0",
"chroma-js": "^3.1.2",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"dotenv": "^17.2.1",
"flag-icons": "^7.5.0",
"glob": "^11.0.3",
"jotai": "^2.13.1",
"dotenv": "^16.4.7",
"flag-icons": "^7.3.2",
"glob": "^11.0.1",
"jotai": "^2.12.2",
"mantine-react-table": "2.0.0-beta.9",
"next": "15.5.0",
"postcss-preset-mantine": "^1.18.0",
"next": "15.2.4",
"postcss-preset-mantine": "^1.17.0",
"prismjs": "^1.30.0",
"react": "19.1.1",
"react-dom": "19.1.1",
"react-error-boundary": "^6.0.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-error-boundary": "^5.0.0",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.90.0",
"sass": "^1.86.0",
"superjson": "2.2.2",
"swagger-ui-react": "^5.27.1",
"swagger-ui-react": "^5.20.2",
"use-deep-compare-effect": "^1.8.1",
"zod": "^4.0.17"
"zod": "^3.24.2"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/chroma-js": "3.1.1",
"@types/node": "^22.17.2",
"@types/node": "^22.13.14",
"@types/prismjs": "^1.26.5",
"@types/react": "19.1.10",
"@types/react-dom": "19.1.7",
"@types/react": "19.0.12",
"@types/react-dom": "19.0.4",
"@types/swagger-ui-react": "^5.18.0",
"concurrently": "^9.2.0",
"eslint": "^9.33.0",
"concurrently": "^9.1.2",
"eslint": "^9.23.0",
"node-loader": "^2.1.0",
"prettier": "^3.6.2",
"typescript": "^5.9.2"
"prettier": "^3.5.3",
"typescript": "^5.8.2"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 595 KiB

@@ -21,6 +21,13 @@ import type { AppRouter } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared";
import { env } from "@homarr/common/env";
import { atom, useSetAtom } from "jotai";
interface WebSocketConnection {
state: 'connecting' | 'connected' | 'error' | 'closed'
}
export const webSocketConnectionAtom = atom<WebSocketConnection>({ state: 'connecting' });
const getWebSocketProtocol = () => {
// window is not defined on server side
@@ -44,9 +51,27 @@ const constructWebsocketUrl = () => {
return `${getWebSocketProtocol()}://${window.location.hostname}:${window.location.port}/websockets`;
};
const wsClient = createWSClient({
url: constructWebsocketUrl(),
});
let wsClient: WsClient | null;
const createWsClientOrDefault = (setState: (connection: WebSocketConnection) => void) => {
if (wsClient) {
return wsClient;
}
wsClient = createWSClient({
url: constructWebsocketUrl(),
onOpen() {
setState({ state: "connected" });
},
onClose() {
setState({ state: "closed" });
},
onError() {
setState({ state: "error" });
}
});
return wsClient;
}
export function TRPCReactProvider(props: PropsWithChildren) {
const [queryClient] = useState(
@@ -60,6 +85,8 @@ export function TRPCReactProvider(props: PropsWithChildren) {
}),
);
const setWebSocketConnection = useSetAtom(webSocketConnectionAtom);
const [trpcClient] = useState(() => {
return clientApi.createClient({
links: [
@@ -70,7 +97,7 @@ export function TRPCReactProvider(props: PropsWithChildren) {
splitLink({
condition: ({ type }) => type === "subscription",
true: wsLink<AppRouter>({
client: wsClient,
client: createWsClientOrDefault((state) => setWebSocketConnection(state)),
transformer: superjson,
}),
false: splitLink({
@@ -106,7 +133,7 @@ export function TRPCReactProvider(props: PropsWithChildren) {
<clientApi.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<ReactQueryStreamedHydration transformer={superjson}>{props.children}</ReactQueryStreamedHydration>
<ReactQueryDevtools initialIsOpen={false} />
<ReactQueryDevtools initialIsOpen={false}/>
</QueryClientProvider>
</clientApi.Provider>
);
@@ -2,14 +2,14 @@
import { useRouter } from "next/navigation";
import { Button, PasswordInput, Stack, TextInput } from "@mantine/core";
import type { z } from "zod/v4";
import type { z } from "zod";
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 { userRegistrationSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
interface RegistrationFormProps {
invite: {
@@ -22,7 +22,7 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
const t = useScopedI18n("user");
const router = useRouter();
const { mutate, isPending } = clientApi.user.register.useMutation();
const form = useZodForm(userRegistrationSchema, {
const form = useZodForm(validation.user.registration, {
initialValues: {
username: "",
password: "",
@@ -30,7 +30,7 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
},
});
const handleSubmit = (values: z.infer<typeof userRegistrationSchema>) => {
const handleSubmit = (values: z.infer<typeof validation.user.registration>) => {
mutate(
{
...values,
@@ -2,10 +2,10 @@
import type { PropsWithChildren } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
import { Anchor, Button, Card, Code, Collapse, Divider, PasswordInput, Stack, Text, TextInput } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { z } from "zod/v4";
import { z } from "zod";
import { signIn } from "@homarr/auth/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
@@ -13,9 +13,7 @@ import type { useForm } from "@homarr/form";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { userSignInSchema } from "@homarr/validation/user";
type Provider = "credentials" | "ldap" | "oidc";
import { validation } from "@homarr/validation";
interface LoginFormProps {
providers: string[];
@@ -24,12 +22,10 @@ interface LoginFormProps {
callbackUrl: string;
}
const extendedValidation = userSignInSchema.extend({ provider: z.enum(["credentials", "ldap"]) });
const extendedValidation = validation.user.signIn.extend({ provider: z.enum(["credentials", "ldap"]) });
export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, callbackUrl }: LoginFormProps) => {
const t = useScopedI18n("user");
const searchParams = useSearchParams();
const isError = searchParams.has("error");
const router = useRouter();
const [isPending, setIsPending] = useState(false);
const form = useZodForm(extendedValidation, {
@@ -43,34 +39,22 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
const credentialInputsVisible = providers.includes("credentials") || providers.includes("ldap");
const onSuccess = useCallback(
async (provider: Provider, response: Awaited<ReturnType<typeof signIn>>) => {
if (!response.ok || response.error) {
async (response: Awaited<ReturnType<typeof signIn>>) => {
if (response && (!response.ok || response.error)) {
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw response.error;
}
if (provider === "oidc") {
if (!response.url) {
showErrorNotification({
title: t("action.login.notification.error.title"),
message: t("action.login.notification.error.message"),
autoClose: 10000,
});
return;
}
router.push(response.url);
return;
}
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.
await revalidatePathActionAsync("/");
router.push(callbackUrl);
if (response) {
await revalidatePathActionAsync("/");
router.push(callbackUrl);
}
},
[t, router, callbackUrl],
);
@@ -86,14 +70,14 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
}, [t]);
const signInAsync = useCallback(
async (provider: Provider, options?: Parameters<typeof signIn>[1]) => {
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((response) => onSuccess(provider, response))
.then(onSuccess)
.catch(onError);
},
[setIsPending, onSuccess, onError, callbackUrl],
@@ -102,12 +86,11 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
const isLoginInProgress = useRef(false);
useEffect(() => {
if (isError) return;
if (isOidcAutoLoginEnabled && !isPending && !isLoginInProgress.current) {
isLoginInProgress.current = true;
void signInAsync("oidc");
}
}, [signInAsync, isOidcAutoLoginEnabled, isPending, isError]);
}, [signInAsync, isOidcAutoLoginEnabled, isPending]);
return (
<Stack gap="xl">
@@ -4,21 +4,15 @@ import { TRPCError } from "@trpc/server";
// Placed here because gridstack styles are used for board content
import "~/styles/gridstack.scss";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { getQueryClient } from "@homarr/api/server";
import { IntegrationProvider } from "@homarr/auth/client";
import { auth } from "@homarr/auth/next";
import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server";
import { isNullOrWhitespace } from "@homarr/common";
import type { WidgetKind } from "@homarr/definitions";
import { logger } from "@homarr/log";
import { getI18n } from "@homarr/translation/server";
import { prefetchForKindAsync } from "@homarr/widgets/prefetch";
import { createMetaTitle } from "~/metadata";
import { createBoardLayout } from "../_layout-creator";
import type { Board, Item } from "../_types";
import type { Board } from "../_types";
import { DynamicClientBoard } from "./_dynamic-client";
import { BoardContentHeaderActions } from "./_header-actions";
@@ -37,36 +31,14 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
getInitialBoardAsync: getInitialBoard,
}),
// eslint-disable-next-line no-restricted-syntax
page: async ({ params }: { params: Promise<TParams> }) => {
page: async () => {
const session = await auth();
const integrations = await getIntegrationsWithPermissionsAsync(session);
const board = await getInitialBoard(await params);
const queryClient = getQueryClient();
// Prefetch item data
const itemsMap = board.items.reduce((acc, item) => {
const existing = acc.get(item.kind);
if (existing) {
existing.push(item);
} else {
acc.set(item.kind, [item]);
}
return acc;
}, new Map<WidgetKind, Item[]>());
for (const [kind, items] of itemsMap) {
await prefetchForKindAsync(kind, queryClient, items).catch((error) => {
logger.error(new Error("Failed to prefetch widget", { cause: error }));
});
}
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<IntegrationProvider integrations={integrations}>
<DynamicClientBoard />
</IntegrationProvider>
</HydrationBoundary>
<IntegrationProvider integrations={integrations}>
<DynamicClientBoard />
</IntegrationProvider>
);
},
generateMetadataAsync: async ({ params }: { params: Promise<TParams> }): Promise<Metadata> => {
@@ -1,5 +1,6 @@
"use client";
import type { MouseEvent } from "react";
import { useCallback, useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
@@ -24,11 +25,9 @@ import { useEditMode } from "@homarr/boards/edit-mode";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { env } from "@homarr/common/env";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { AppSelectModal } from "@homarr/modals-collection";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useItemActions } from "~/components/board/items/item-actions";
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";
@@ -63,10 +62,8 @@ export const BoardContentHeaderActions = () => {
const AddMenu = () => {
const { openModal: openCategoryEditModal } = useModalAction(CategoryEditModal);
const { openModal: openItemSelectModal } = useModalAction(ItemSelectModal);
const { openModal: openAppSelectModal } = useModalAction(AppSelectModal);
const { addCategoryToEnd } = useCategoryActions();
const { addDynamicSection } = useDynamicSectionActions();
const { createItem } = useItemActions();
const t = useI18n();
const handleAddCategory = useCallback(
@@ -93,17 +90,6 @@ const AddMenu = () => {
openItemSelectModal();
}, [openItemSelectModal]);
const handleSelectApp = useCallback(() => {
openAppSelectModal({
onSelect: (appId) => {
createItem({
kind: "app",
options: { appId },
});
},
});
}, [openAppSelectModal, createItem]);
return (
<Menu position="bottom-end" withArrow>
<Menu.Target>
@@ -115,14 +101,10 @@ const AddMenu = () => {
</HeaderButton>
</Menu.Target>
<Menu.Dropdown style={{ transform: "translate(-3px, 0)" }}>
<Menu.Item leftSection={<IconResize size={20} />} onClick={handleSelectItem}>
<Menu.Item leftSection={<IconBox size={20} />} onClick={handleSelectItem}>
{t("item.action.create")}
</Menu.Item>
<Menu.Item leftSection={<IconBox size={20} />} onClick={handleSelectApp}>
{t("app.action.add")}
</Menu.Item>
<Menu.Divider />
<Menu.Item leftSection={<IconBoxAlignTop size={20} />} onClick={handleAddCategory}>
@@ -203,22 +185,17 @@ const SelectBoardsMenu = () => {
);
};
const anchorSelector = "a[href]:not([target='_blank'])";
const usePreventLeaveWithDirty = (isDirty: boolean) => {
const t = useI18n();
const { openConfirmModal } = useConfirmModal();
const router = useRouter();
useEffect(() => {
if (!isDirty) return;
const handleClick = (event: Event) => {
const handleClick = (event: MouseEvent<HTMLElement>) => {
const target = (event.target as HTMLElement).closest("a");
if (!target) {
console.warn("No anchor element found for click event", event);
return;
}
if (!target) return;
if (!isDirty) return;
event.preventDefault();
@@ -235,29 +212,33 @@ const usePreventLeaveWithDirty = (isDirty: boolean) => {
};
const handlePopState = (event: Event) => {
window.history.pushState(null, document.title, window.location.href);
event.preventDefault();
if (isDirty) {
window.history.pushState(null, document.title, window.location.href);
event.preventDefault();
} else {
window.history.back();
}
};
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
if (!isDirty) return;
if (env.NODE_ENV === "development") return; // Allow to reload in development
event.preventDefault();
event.returnValue = true;
};
const anchors = document.querySelectorAll(anchorSelector);
anchors.forEach((link) => {
link.addEventListener("click", handleClick);
document.querySelectorAll("a").forEach((link) => {
link.addEventListener("click", handleClick as never);
});
window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
anchors.forEach((link) => {
link.removeEventListener("click", handleClick);
document.querySelectorAll("a").forEach((link) => {
link.removeEventListener("click", handleClick as never);
window.removeEventListener("popstate", handlePopState);
});
window.removeEventListener("popstate", handlePopState);
window.removeEventListener("beforeunload", handleBeforeUnload);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -2,7 +2,7 @@
import type { PropsWithChildren } from "react";
import type { MantineColorsTuple } from "@mantine/core";
import { colorsTuple, createTheme, darken, lighten, MantineProvider, rem } from "@mantine/core";
import { colorsTuple, createTheme, darken, lighten, MantineProvider } from "@mantine/core";
import { useRequiredBoard } from "@homarr/boards/context";
import type { ColorScheme } from "@homarr/definitions";
@@ -24,11 +24,6 @@ export const BoardMantineProvider = ({
},
primaryColor: "primaryColor",
autoContrast: true,
fontSizes: {
"2xl": rem(24),
"3xl": rem(28),
"4xl": rem(36),
},
});
return (
@@ -17,11 +17,10 @@ import {
useMantineTheme,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconX } from "@tabler/icons-react";
import { useZodForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";
import { boardSavePartialSettingsSchema } from "@homarr/validation/board";
import { validation } from "@homarr/validation";
import type { Board } from "../../_types";
import { generateColors } from "../../(content)/_theme";
@@ -36,7 +35,7 @@ const hexRegex = /^#[0-9a-fA-F]{6}$/;
const progressPercentageLabel = (value: number) => `${value}%`;
export const ColorSettingsContent = ({ board }: Props) => {
const form = useZodForm(boardSavePartialSettingsSchema, {
const form = useZodForm(validation.board.savePartialSettings, {
initialValues: {
primaryColor: board.primaryColor,
secondaryColor: board.secondaryColor,
@@ -103,25 +102,12 @@ export const ColorSettingsContent = ({ board }: Props) => {
</InputWrapper>
</Grid.Col>
<Grid.Col span={{ sm: 12, md: 6 }}>
<Group align="end">
<ColorInput
label={t("board.field.iconColor.label")}
format="hex"
swatches={Object.values(theme.colors).map((color) => color[6])}
flex={1}
{...form.getInputProps("iconColor")}
/>
<Button
variant="subtle"
leftSection={<IconX />}
onClick={() => form.setFieldValue("iconColor", "")}
disabled={!form.values.iconColor}
>
{t("board.field.clearColor.label")}
</Button>
</Group>
</Grid.Col>
<Grid.Col span={{ sm: 12, md: 6 }}>
<ColorInput
label={t("board.field.iconColor.label")}
format="hex"
swatches={Object.values(theme.colors).map((color) => color[6])}
{...form.getInputProps("iconColor")}
/>
<Select
label={t("board.field.itemRadius.label")}
description={t("board.field.itemRadius.description")}
@@ -137,7 +123,7 @@ export const ColorSettingsContent = ({ board }: Props) => {
</Grid.Col>
</Grid>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -1,20 +1,14 @@
"use client";
import { startTransition } from "react";
import { ActionIcon, Autocomplete, Button, Center, Grid, Group, Popover, Stack, Text } from "@mantine/core";
import { useDebouncedValue } from "@mantine/hooks";
import { IconPhotoOff, IconUpload } from "@tabler/icons-react";
import { Button, Grid, Group, Stack, TextInput } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { backgroundImageAttachments, backgroundImageRepeats, backgroundImageSizes } from "@homarr/definitions";
import { useZodForm } from "@homarr/form";
import { UploadMedia } from "@homarr/forms-collection";
import type { TranslationObject } from "@homarr/translation";
import { useI18n } from "@homarr/translation/client";
import type { SelectItemWithDescriptionBadge } from "@homarr/ui";
import { SelectWithDescriptionBadge } from "@homarr/ui";
import { boardSavePartialSettingsSchema } from "@homarr/validation/board";
import { validation } from "@homarr/validation";
import type { Board } from "../../_types";
import { useSavePartialSettingsMutation } from "./_shared";
@@ -24,9 +18,8 @@ interface Props {
}
export const BackgroundSettingsContent = ({ board }: Props) => {
const t = useI18n();
const { data: session } = useSession();
const { mutate: savePartialSettings, isPending } = useSavePartialSettingsMutation(board);
const form = useZodForm(boardSavePartialSettingsSchema, {
const form = useZodForm(validation.board.savePartialSettings, {
initialValues: {
backgroundImageUrl: board.backgroundImageUrl ?? "",
backgroundImageAttachment: board.backgroundImageAttachment,
@@ -35,16 +28,6 @@ export const BackgroundSettingsContent = ({ board }: Props) => {
},
});
const [debouncedSearch] = useDebouncedValue(form.values.backgroundImageUrl, 200);
const medias = clientApi.media.getPaginated.useQuery({
page: 1,
pageSize: 10,
includeFromAllUsers: true,
search: debouncedSearch ?? "",
});
const images = medias.data?.items.filter((media) => media.contentType.startsWith("image/")) ?? [];
const imageMap = new Map(images.map((image) => [`/api/user-medias/${image.id}`, image]));
const backgroundImageAttachmentData = useBackgroundOptionData(
"backgroundImageAttachment",
backgroundImageAttachments,
@@ -64,76 +47,10 @@ export const BackgroundSettingsContent = ({ board }: Props) => {
<Stack>
<Grid>
<Grid.Col span={12}>
<Group wrap="nowrap" gap="xs" w="100%" align="start">
<Autocomplete
flex={1}
leftSection={
form.values.backgroundImageUrl &&
form.values.backgroundImageUrl.trim().length >= 2 && (
<Popover width={300} withArrow>
<Popover.Target>
<Center h="100%">
<ImagePreview src={form.values.backgroundImageUrl} w={20} h={20} />
</Center>
</Popover.Target>
<Popover.Dropdown>
<ImagePreview src={form.values.backgroundImageUrl} w="100%" />
</Popover.Dropdown>
</Popover>
)
}
// We filter it on the server
filter={({ options }) => options}
label={t("board.field.backgroundImageUrl.label")}
placeholder={`${t("board.field.backgroundImageUrl.placeholder")}...`}
renderOption={({ option }) => {
const current = imageMap.get(option.value);
if (!current) return null;
return (
<Group gap="sm">
<ImagePreview src={option.value} w={20} h={20} />
<Stack gap={0}>
<Text size="sm">{current.name}</Text>
<Text size="xs" c="dimmed">
{option.value}
</Text>
</Stack>
</Group>
);
}}
data={[
{
group: t("board.field.backgroundImageUrl.group.your"),
items: images
.filter((media) => media.creatorId === session?.user.id)
.map((media) => `/api/user-medias/${media.id}`),
},
{
group: t("board.field.backgroundImageUrl.group.other"),
items: images
.filter((media) => media.creatorId !== session?.user.id)
.map((media) => `/api/user-medias/${media.id}`),
},
]}
{...form.getInputProps("backgroundImageUrl")}
/>
{session?.user.permissions.includes("media-upload") && (
<UploadMedia
onSuccess={({ url }) =>
startTransition(() => {
form.setFieldValue("backgroundImageUrl", url);
})
}
>
{({ onClick, loading }) => (
<ActionIcon onClick={onClick} loading={loading} mt={24} size={36} variant="default">
<IconUpload size={16} stroke={1.5} />
</ActionIcon>
)}
</UploadMedia>
)}
</Group>
<TextInput
label={t("board.field.backgroundImageUrl.label")}
{...form.getInputProps("backgroundImageUrl")}
/>
</Grid.Col>
<Grid.Col span={12}>
<SelectWithDescriptionBadge
@@ -159,7 +76,7 @@ export const BackgroundSettingsContent = ({ board }: Props) => {
</Grid>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -168,21 +85,6 @@ export const BackgroundSettingsContent = ({ board }: Props) => {
);
};
interface ImagePreviewProps {
src: string;
w: string | number;
h?: string | number;
}
const ImagePreview = ({ src, w, h }: ImagePreviewProps) => {
if (!["/", "http://", "https://"].some((prefix) => src.startsWith(prefix))) {
return <IconPhotoOff size={w} />;
}
// eslint-disable-next-line @next/next/no-img-element
return <img src={src} alt="preview image" style={{ width: w, height: h, objectFit: "contain" }} />;
};
type BackgroundImageKey = "backgroundImageAttachment" | "backgroundImageSize" | "backgroundImageRepeat";
type inferOptions<TKey extends BackgroundImageKey> = TranslationObject["board"]["field"][TKey]["option"];
@@ -38,7 +38,7 @@ export const BehaviorSettingsContent = ({ board }: Props) => {
/>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -46,7 +46,7 @@ export const CustomCssSettingsContent = ({ board }: Props) => {
</Alert>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -1,14 +1,14 @@
"use client";
import { useEffect, useRef } from "react";
import { Button, Grid, Group, Loader, Stack, TextInput } from "@mantine/core";
import { Button, Grid, Group, Loader, Stack, TextInput, Tooltip } from "@mantine/core";
import { useDebouncedValue, useDocumentTitle, useFavicon } from "@mantine/hooks";
import { IconAlertTriangle } from "@tabler/icons-react";
import { useUpdateBoard } from "@homarr/boards/updater";
import { useZodForm } from "@homarr/form";
import { IconPicker } from "@homarr/forms-collection";
import { useI18n } from "@homarr/translation/client";
import { boardSavePartialSettingsSchema } from "@homarr/validation/board";
import { validation } from "@homarr/validation";
import { createMetaTitle } from "~/metadata";
import type { Board } from "../../_types";
@@ -28,7 +28,7 @@ export const GeneralSettingsContent = ({ board }: Props) => {
const { mutate: savePartialSettings, isPending } = useSavePartialSettingsMutation(board);
const form = useZodForm(
boardSavePartialSettingsSchema
validation.board.savePartialSettings
.pick({
pageTitle: true,
logoImageUrl: true,
@@ -52,9 +52,9 @@ export const GeneralSettingsContent = ({ board }: Props) => {
},
);
useLogoPreview(form.values.logoImageUrl);
useFaviconPreview(form.values.faviconImageUrl);
const metaTitleStatus = useMetaTitlePreview(form.values.metaTitle);
const faviconStatus = useFaviconPreview(form.values.faviconImageUrl);
const logoStatus = useLogoPreview(form.values.logoImageUrl);
// Cleanup for not applied changes of the page title and logo image URL
useEffect(() => {
@@ -94,29 +94,29 @@ export const GeneralSettingsContent = ({ board }: Props) => {
<TextInput
label={t("board.field.metaTitle.label")}
placeholder={createMetaTitle(t("board.content.metaTitle", { boardName: board.name }))}
rightSection={metaTitleStatus.isPending && <Loader size="xs" />}
rightSection={<PendingOrInvalidIndicator {...metaTitleStatus} />}
{...form.getInputProps("metaTitle")}
/>
</Grid.Col>
<Grid.Col span={{ xs: 12, md: 6 }}>
<IconPicker
{...form.getInputProps("logoImageUrl")}
<TextInput
label={t("board.field.logoImageUrl.label")}
placeholder="/logo/logo.png"
withAsterisk={false}
rightSection={<PendingOrInvalidIndicator {...logoStatus} />}
{...form.getInputProps("logoImageUrl")}
/>
</Grid.Col>
<Grid.Col span={{ xs: 12, md: 6 }}>
<IconPicker
{...form.getInputProps("faviconImageUrl")}
<TextInput
label={t("board.field.faviconImageUrl.label")}
placeholder="/logo/logo.png"
withAsterisk={false}
rightSection={<PendingOrInvalidIndicator {...faviconStatus} />}
{...form.getInputProps("faviconImageUrl")}
/>
</Grid.Col>
</Grid>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -125,16 +125,40 @@ export const GeneralSettingsContent = ({ board }: Props) => {
);
};
const PendingOrInvalidIndicator = ({ isPending, isInvalid }: { isPending: boolean; isInvalid?: boolean }) => {
const t = useI18n();
if (isInvalid) {
return (
<Tooltip multiline w={220} label={t("board.setting.section.general.unrecognizedLink")}>
<IconAlertTriangle size="1rem" color="red" />
</Tooltip>
);
}
if (isPending) {
return <Loader size="xs" />;
}
return null;
};
const useLogoPreview = (url: string | null) => {
const { updateBoard } = useUpdateBoard();
const [logoDebounced] = useDebouncedValue(url ?? "", 500);
useEffect(() => {
if (!logoDebounced.includes(".") && logoDebounced.length >= 1) return;
updateBoard((previous) => ({
...previous,
logoImageUrl: logoDebounced.length >= 1 ? logoDebounced : null,
}));
}, [logoDebounced, updateBoard]);
return {
isPending: (url ?? "") !== logoDebounced,
isInvalid: logoDebounced.length >= 1 && !logoDebounced.includes("."),
};
};
const useMetaTitlePreview = (title: string | null) => {
@@ -146,7 +170,16 @@ const useMetaTitlePreview = (title: string | null) => {
};
};
const validFaviconExtensions = ["ico", "png", "svg", "gif"];
const isValidUrl = (url: string) =>
url.includes("/") && validFaviconExtensions.some((extension) => url.endsWith(`.${extension}`));
const useFaviconPreview = (url: string | null) => {
const [faviconDebounced] = useDebouncedValue(url ?? "", 500);
useFavicon(faviconDebounced);
useFavicon(isValidUrl(faviconDebounced) ? faviconDebounced : "");
return {
isPending: (url ?? "") !== faviconDebounced,
isInvalid: faviconDebounced.length >= 1 && !isValidUrl(faviconDebounced),
};
};
@@ -3,10 +3,10 @@
import { Button, Fieldset, Grid, Group, Input, NumberInput, Slider, Stack, Text, TextInput } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/common";
import { createId } from "@homarr/db/client";
import { useZodForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";
import { boardSaveLayoutsSchema } from "@homarr/validation/board";
import { validation } from "@homarr/validation";
import type { Board } from "../../_types";
@@ -22,7 +22,7 @@ export const LayoutSettingsContent = ({ board }: Props) => {
void utils.board.getHomeBoard.invalidate();
},
});
const form = useZodForm(boardSaveLayoutsSchema.omit({ id: true }).required(), {
const form = useZodForm(validation.board.saveLayouts.omit({ id: true }).required(), {
initialValues: {
layouts: board.layouts,
},
@@ -105,7 +105,7 @@ export const LayoutSettingsContent = ({ board }: Props) => {
</Stack>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -2,24 +2,24 @@
import { Button, Card, Stack, TextInput } from "@mantine/core";
import { IconArrowRight } from "@tabler/icons-react";
import type { z } from "zod/v4";
import type { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";
import { groupCreateSchema } from "@homarr/validation/group";
import { validation } from "@homarr/validation";
export const InitGroup = () => {
const t = useI18n();
const { mutateAsync } = clientApi.group.createInitialExternalGroup.useMutation();
const form = useZodForm(groupCreateSchema, {
const form = useZodForm(validation.group.create, {
initialValues: {
name: "",
},
});
const handleSubmitAsync = async (values: z.infer<typeof groupCreateSchema>) => {
const handleSubmitAsync = async (values: z.infer<typeof validation.group.create>) => {
await mutateAsync(values, {
async onSuccess() {
await revalidatePathActionAsync("/init");
@@ -3,7 +3,7 @@
import { startTransition } from "react";
import { Button, Card, Group, Stack, Switch, Text } from "@mantine/core";
import { IconArrowRight } from "@tabler/icons-react";
import type { z } from "zod/v4";
import type { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
@@ -12,13 +12,13 @@ import type { CheckboxProps } from "@homarr/form/types";
import { defaultServerSettings } from "@homarr/server-settings";
import type { TranslationObject } from "@homarr/translation";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { settingsInitSchema } from "@homarr/validation/settings";
import { validation } from "@homarr/validation";
export const InitSettings = () => {
const tSection = useScopedI18n("management.page.settings.section");
const t = useI18n();
const { mutateAsync } = clientApi.serverSettings.initSettings.useMutation();
const form = useZodForm(settingsInitSchema, { initialValues: defaultServerSettings });
const form = useZodForm(validation.settings.init, { initialValues: defaultServerSettings });
form.watch("analytics.enableGeneral", ({ value }) => {
if (!value) {
@@ -30,7 +30,7 @@ export const InitSettings = () => {
}
});
const handleSubmitAsync = async (values: z.infer<typeof settingsInitSchema>) => {
const handleSubmitAsync = async (values: z.infer<typeof validation.settings.init>) => {
await mutateAsync(values, {
async onSuccess() {
await revalidatePathActionAsync("/init");
@@ -1,7 +1,7 @@
"use client";
import { Button, PasswordInput, Stack, TextInput } from "@mantine/core";
import type { z } from "zod/v4";
import type { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { signIn } from "@homarr/auth/client";
@@ -10,13 +10,13 @@ import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { CustomPasswordInput } from "@homarr/ui";
import { userInitSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
export const InitUserForm = () => {
const t = useScopedI18n("user");
const tUser = useScopedI18n("init.step.user");
const { mutateAsync, isPending } = clientApi.user.initUser.useMutation();
const form = useZodForm(userInitSchema, {
const form = useZodForm(validation.user.init, {
initialValues: {
username: "",
password: "",
@@ -74,4 +74,4 @@ export const InitUserForm = () => {
);
};
type FormType = z.infer<typeof userInitSchema>;
type FormType = z.infer<typeof validation.user.init>;
+1 -10
View File
@@ -7,7 +7,6 @@ import "@homarr/ui/styles.css";
import "~/styles/scroll-area.scss";
import { notFound } from "next/navigation";
import type { DayOfWeek } from "@mantine/dates";
import { NextIntlClientProvider } from "next-intl";
import { api } from "@homarr/api/server";
@@ -88,15 +87,7 @@ export default async function Layout(props: {
},
(innerProps) => (
<SettingsProvider
user={
user
? {
...user,
// Convert type, because output schema is not smart enough to infer $type from drizzle
firstDayOfWeek: user.firstDayOfWeek as DayOfWeek,
}
: null
}
user={user}
serverSettings={{
board: {
homeBoardId: serverSettings.board.homeBoardId,
@@ -1,16 +1,37 @@
import { Box, Grid, GridCol, Group, Image, Stack, Title } from "@mantine/core";
import { splitToNChunks } from "@homarr/common";
import { integrationDefs } from "@homarr/definitions";
import classes from "./hero-banner.module.css";
const icons = Object.values(integrationDefs)
.filter((int) => int.name !== "Mock")
.map((int) => int.iconUrl);
const icons = [
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/sabnzbd.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/deluge.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/radarr.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/sonarr.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/lidarr.svg",
"https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/pihole.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/dashdot.png",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/overseerr.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/plex.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyfin.svg",
"https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/homeassistant.svg",
"https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/freshrss.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/readarr.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/transmission.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/qbittorrent.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/nzbget.png",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/openmediavault.svg",
"https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/docker.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyseerr.svg",
"https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/adguardhome.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/tdarr.png",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/prowlarr.svg",
];
const countIconGroups = 3;
const animationDurationInSeconds = icons.length;
const animationDurationInSeconds = 12;
const arrayInChunks = splitToNChunks(icons, countIconGroups);
export const HeroBanner = () => {
@@ -40,26 +61,12 @@ export const HeroBanner = () => {
}}
>
{arrayInChunks[columnIndex]?.map((icon, index) => (
<Image
key={`grid-column-${columnIndex}-scroll-1-${index}`}
src={icon}
radius="md"
fit={"contain"}
w={50}
h={50}
/>
<Image key={`grid-column-${columnIndex}-scroll-1-${index}`} src={icon} radius="md" w={50} h={50} />
))}
{/* This is used for making the animation seem seamless */}
{arrayInChunks[columnIndex]?.map((icon, index) => (
<Image
key={`grid-column-${columnIndex}-scroll-2-${index}`}
src={icon}
radius="md"
fit={"contain"}
w={50}
h={50}
/>
<Image key={`grid-column-${columnIndex}-scroll-2-${index}`} src={icon} radius="md" w={50} h={50} />
))}
</Stack>
</GridCol>
@@ -2,7 +2,7 @@
import { useCallback } from "react";
import { useRouter } from "next/navigation";
import type { z } from "zod/v4";
import type { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -10,7 +10,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { AppForm } from "@homarr/forms-collection";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { appManageSchema } from "@homarr/validation/app";
import type { validation } from "@homarr/validation";
interface AppEditFormProps {
app: RouterOutputs["app"]["byId"];
@@ -40,7 +40,7 @@ export const AppEditForm = ({ app }: AppEditFormProps) => {
});
const handleSubmit = useCallback(
(values: z.infer<typeof appManageSchema>) => {
(values: z.infer<typeof validation.app.manage>) => {
mutate({
id: app.id,
...values,
@@ -1,9 +1,8 @@
import { Fragment } from "react";
import Link from "next/link";
import { redirect } from "next/navigation";
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
import { IconBox, IconPencil } from "@tabler/icons-react";
import { z } from "zod/v4";
import { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
@@ -99,12 +98,7 @@ const AppCard = async ({ app }: AppCardProps) => {
</Text>
{app.description && (
<Text size="sm" c="gray.6" lineClamp={4}>
{app.description.split("\n").map((line, index) => (
<Fragment key={index}>
{line}
<br />
</Fragment>
))}
{app.description}
</Text>
)}
{app.href && (
@@ -1,12 +1,4 @@
import {
IconGrid3x3,
IconKey,
IconMessage,
IconPassword,
IconPasswordUser,
IconServer,
IconUser,
} from "@tabler/icons-react";
import { IconGrid3x3, IconKey, IconPassword, IconServer, IconUser } from "@tabler/icons-react";
import type { IntegrationSecretKind } from "@homarr/definitions";
import type { TablerIcon } from "@homarr/ui";
@@ -17,8 +9,4 @@ export const integrationSecretIcons = {
password: IconPassword,
realm: IconServer,
tokenId: IconGrid3x3,
personalAccessToken: IconPasswordUser,
topic: IconMessage,
opnsenseApiKey: IconKey,
opnsenseApiSecret: IconPassword,
} satisfies Record<IntegrationSecretKind, TablerIcon>;
@@ -1,96 +0,0 @@
import { useMemo } from "react";
import { Accordion, Anchor, Card, Stack, Text } from "@mantine/core";
import { IconSubtask } from "@tabler/icons-react";
import { getMantineColor } from "@homarr/common";
import { useI18n } from "@homarr/translation/client";
import { CertificateErrorDetails } from "./test-connection-certificate";
import type { AnyMappedTestConnectionError, MappedError } from "./types";
interface IntegrationTestConnectionErrorProps {
error: AnyMappedTestConnectionError;
url: string;
}
export const IntegrationTestConnectionError = ({ error, url }: IntegrationTestConnectionErrorProps) => {
const t = useI18n();
const causeArray = useMemo(() => toCauseArray(error.cause), [error.cause]);
return (
<Card withBorder style={{ borderColor: getMantineColor("red", 8) }}>
<Stack>
<Stack gap="sm">
<Text size="lg" fw={500} c="red.8">
{t(`integration.testConnection.error.${error.type}.title`)}
</Text>
{error.type !== "request" && error.type !== "certificate" && error.type !== "statusCode" ? (
<Text size="md">{t(`integration.testConnection.error.${error.type}.description`)}</Text>
) : null}
{error.type === "request" ? (
<Text size="md">
{t(
`integration.testConnection.error.request.description.${error.data.type}.${error.data.reason}` as never,
)}
</Text>
) : null}
{error.type === "statusCode" ? (
error.data.reason === "other" ? (
<Text size="md">
{t.rich("integration.testConnection.error.statusCode.otherDescription", {
statusCode: error.data.statusCode.toString(),
url: () => <Anchor href={error.data.url}>{error.data.url}</Anchor>,
})}
</Text>
) : (
<Text size="md">
{t.rich("integration.testConnection.error.statusCode.description", {
reason: t(`integration.testConnection.error.statusCode.reason.${error.data.reason}`),
statusCode: error.data.statusCode.toString(),
url: () => <Anchor href={error.data.url}>{error.data.url}</Anchor>,
})}
</Text>
)
) : null}
{error.type === "certificate" ? <CertificateErrorDetails error={error} url={url} /> : null}
</Stack>
{error.cause ? (
<Accordion variant="contained">
<Accordion.Item value="cause">
<Accordion.Control icon={<IconSubtask size={16} stroke={1.5} />}>
{t("integration.testConnection.error.common.cause.title")}
</Accordion.Control>
<Accordion.Panel>
<pre style={{ whiteSpace: "pre-wrap" }}>
{error.name}: {error.message}
{"\n"}
{causeArray
.map(
(cause) =>
`caused by ${cause.name}${cause.message ? `: ${cause.message}` : ""} ${cause.metadata.map((item) => `${item.key}=${item.value}`).join(" ")}`,
)
.join("\n")}
</pre>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
) : null}
</Stack>
</Card>
);
};
const toCauseArray = (cause: MappedError | undefined) => {
const causeArray: MappedError[] = [];
let currentCause: MappedError | undefined = cause;
while (currentCause) {
causeArray.push(currentCause);
currentCause = currentCause.cause;
}
return causeArray.map(({ cause: _innerCause, ...cause }) => cause);
};
@@ -1,333 +0,0 @@
import { useState } from "react";
import { ActionIcon, Alert, Anchor, Button, Card, CopyButton, Group, SimpleGrid, Stack, Text } from "@mantine/core";
import { IconAlertTriangle, IconCheck, IconCopy, IconExclamationCircle, IconRepeat } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { createId, getMantineColor } from "@homarr/common";
import { createDocumentationLink } from "@homarr/definitions";
import { createModal, useConfirmModal, useModalAction } from "@homarr/modals";
import { AddCertificateModal } from "@homarr/modals-collection";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useCurrentLocale, useI18n, useScopedI18n } from "@homarr/translation/client";
import type { MappedCertificate, MappedTestConnectionCertificateError } from "./types";
interface CertificateErrorDetailsProps {
error: MappedTestConnectionCertificateError;
url: string;
}
export const CertificateErrorDetails = ({ error, url }: CertificateErrorDetailsProps) => {
const tError = useScopedI18n("integration.testConnection.error");
const { data: session } = useSession();
const isAdmin = session?.user.permissions.includes("admin") ?? false;
const [showRetryButton, setShowRetryButton] = useState(false);
const { openModal: openUploadModal } = useModalAction(AddCertificateModal);
const { openConfirmModal } = useConfirmModal();
const { mutateAsync: trustHostnameAsync } = clientApi.certificates.trustHostnameMismatch.useMutation();
const { mutateAsync: addCertificateAsync } = clientApi.certificates.addCertificate.useMutation();
const rootCertificate = getHeighestCertificate(error.data.certificate);
const handleTrustHostname = () => {
const { hostname } = new URL(url);
openConfirmModal({
title: tError("certificate.hostnameMismatch.confirm.title"),
children: tError("certificate.hostnameMismatch.confirm.message"),
// eslint-disable-next-line no-restricted-syntax
async onConfirm() {
await trustHostnameAsync(
{
hostname,
certificate: error.data.certificate.pem,
},
{
onSuccess() {
showSuccessNotification({
title: tError("certificate.hostnameMismatch.notification.success.title"),
message: tError("certificate.hostnameMismatch.notification.success.message"),
});
setShowRetryButton(true);
},
onError() {
showErrorNotification({
title: tError("certificate.hostnameMismatch.notification.error.title"),
message: tError("certificate.hostnameMismatch.notification.error.message"),
});
},
},
);
},
});
};
const handleTrustSelfSigned = () => {
const { hostname } = new URL(url);
openConfirmModal({
title: tError("certificate.selfSigned.confirm.title"),
children: tError("certificate.selfSigned.confirm.message"),
// eslint-disable-next-line no-restricted-syntax
async onConfirm() {
const formData = new FormData();
formData.append(
"file",
new File([rootCertificate.pem], `${hostname}-${createId()}.crt`, {
type: "application/x-x509-ca-cert",
}),
);
await addCertificateAsync(formData, {
onSuccess() {
showSuccessNotification({
title: tError("certificate.selfSigned.notification.success.title"),
message: tError("certificate.selfSigned.notification.success.message"),
});
setShowRetryButton(true);
},
onError() {
showErrorNotification({
title: tError("certificate.selfSigned.notification.error.title"),
message: tError("certificate.selfSigned.notification.error.message"),
});
},
});
},
});
};
const description = <Text size="md">{tError(`certificate.description.${error.data.reason}`)}</Text>;
if (!isAdmin) {
return (
<>
{description}
<NotEnoughPermissionsAlert />
</>
);
}
return (
<>
{description}
<CertificateDetailsCard certificate={rootCertificate} />
{error.data.reason === "hostnameMismatch" && <HostnameMismatchAlert />}
{!rootCertificate.isSelfSigned && error.data.reason === "untrusted" && <CertificateExtractAlert />}
{showRetryButton && (
<Button
variant="default"
fullWidth
leftSection={<IconRepeat size={16} color={getMantineColor("blue", 6)} stroke={1.5} />}
type="submit"
>
{tError("certificate.action.retry.label")}
</Button>
)}
{(error.data.reason === "untrusted" && rootCertificate.isSelfSigned) ||
error.data.reason === "hostnameMismatch" ? (
<Button
variant="default"
fullWidth
onClick={error.data.reason === "hostnameMismatch" ? handleTrustHostname : handleTrustSelfSigned}
>
{tError("certificate.action.trust.label")}
</Button>
) : null}
{error.data.reason === "untrusted" && !rootCertificate.isSelfSigned ? (
<Button
variant="default"
fullWidth
onClick={() =>
openUploadModal({
onSuccess() {
setShowRetryButton(true);
},
})
}
>
{tError("certificate.action.upload.label")}
</Button>
) : null}
</>
);
};
const NotEnoughPermissionsAlert = () => {
const t = useI18n();
return (
<Alert
icon={<IconAlertTriangle size={16} />}
title={t("integration.testConnection.error.certificate.alert.permission.title")}
color="yellow"
>
{t("integration.testConnection.error.certificate.alert.permission.message")}
</Alert>
);
};
const HostnameMismatchAlert = () => {
const t = useI18n();
return (
<Alert
icon={<IconAlertTriangle size={16} />}
title={t("integration.testConnection.error.certificate.alert.hostnameMismatch.title")}
color="yellow"
>
{t("integration.testConnection.error.certificate.alert.hostnameMismatch.message")}
</Alert>
);
};
const CertificateExtractAlert = () => {
const t = useI18n();
return (
<Alert
icon={<IconExclamationCircle size={16} />}
title={t("integration.testConnection.error.certificate.alert.extract.title")}
color="red"
>
{t.rich("integration.testConnection.error.certificate.alert.extract.message", {
docsLink: () => (
<Anchor
href={createDocumentationLink("/docs/management/certificates", "#obtaining-certificates")}
target="_blank"
>
{t("common.here")}
</Anchor>
),
})}
</Alert>
);
};
interface CertificateDetailsProps {
certificate: MappedCertificate;
}
export const CertificateDetailsCard = ({ certificate }: CertificateDetailsProps) => {
const { openModal } = useModalAction(PemContentModal);
const locale = useCurrentLocale();
const tDetails = useScopedI18n("integration.testConnection.error.certificate.details");
const tCertificateField = useScopedI18n("certificate.field");
return (
<Card withBorder>
<Text fw={500}>{tDetails("title")}</Text>
<Group justify="space-between">
<Text size="sm" c="dimmed">
{tDetails("description")}
</Text>
<Anchor
size="sm"
ta="start"
component="button"
type="button"
onClick={() => openModal({ content: certificate.pem })}
>
{tDetails("content.action")}
</Anchor>
</Group>
<SimpleGrid cols={{ base: 1, md: 2 }} mt="md">
<Stack gap={0}>
<Text size="xs" c="dimmed">
{tCertificateField("subject.label")}
</Text>
<Text size="sm">{certificate.subject}</Text>
</Stack>
<Stack gap={0}>
<Text size="xs" c="dimmed">
{tCertificateField("issuer.label")}
</Text>
<Text size="sm">{certificate.issuer}</Text>
</Stack>
<Stack gap={0}>
<Text size="xs" c="dimmed">
{tCertificateField("validFrom.label")}
</Text>
<Text size="sm">
{new Intl.DateTimeFormat(locale, {
dateStyle: "full",
timeStyle: "long",
}).format(certificate.validFrom)}
</Text>
</Stack>
<Stack gap={0}>
<Text size="xs" c="dimmed">
{tCertificateField("validTo.label")}
</Text>
<Text size="sm">
{new Intl.DateTimeFormat(locale, {
dateStyle: "full",
timeStyle: "long",
}).format(certificate.validTo)}
</Text>
</Stack>
<Stack gap={0}>
<Text size="xs" c="dimmed">
{tCertificateField("serialNumber.label")}
</Text>
<Text size="sm">{certificate.serialNumber}</Text>
</Stack>
</SimpleGrid>
<SimpleGrid cols={1} mt="md">
<Stack gap={0}>
<Text size="xs" c="dimmed">
{tCertificateField("fingerprint.label")}
</Text>
<Text size="sm">{certificate.fingerprint}</Text>
</Stack>
</SimpleGrid>
</Card>
);
};
const PemContentModal = createModal<{ content: string }>(({ actions, innerProps }) => {
const t = useI18n();
return (
<Stack>
<Card w="100%" pos="relative" bg="dark.6" fz="xs" p="sm">
<pre
style={{
whiteSpace: "pre-wrap",
wordWrap: "break-word",
}}
>
{innerProps.content}
</pre>
<CopyButton value={innerProps.content}>
{({ copy, copied }) => (
<ActionIcon onClick={copy} pos="absolute" top={8} right={8} variant="default">
{copied ? (
<IconCheck size={16} stroke={1.5} color={getMantineColor("green", 6)} />
) : (
<IconCopy size={16} stroke={1.5} />
)}
</ActionIcon>
)}
</CopyButton>
</Card>
<Button variant="light" color="gray" onClick={actions.closeModal}>
{t("common.action.close")}
</Button>
</Stack>
);
}).withOptions({
defaultTitle(t) {
return t("integration.testConnection.error.certificate.details.content.title");
},
size: "lg",
});
const getHeighestCertificate = (certificate: MappedCertificate): MappedCertificate => {
if (certificate.issuerCertificate) return getHeighestCertificate(certificate.issuerCertificate);
return certificate;
};
@@ -1,6 +0,0 @@
import type { RouterOutputs } from "@homarr/api";
export type AnyMappedTestConnectionError = Exclude<RouterOutputs["integration"]["create"], undefined>["error"];
export type MappedTestConnectionCertificateError = Extract<AnyMappedTestConnectionError, { type: "certificate" }>;
export type MappedCertificate = MappedTestConnectionCertificateError["data"]["certificate"];
export type MappedError = Exclude<AnyMappedTestConnectionError["cause"], undefined>;
@@ -1,25 +1,23 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { Button, Fieldset, Group, Stack, TextInput } from "@mantine/core";
import type { z } from "zod/v4";
import type { z } from "zod";
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";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { integrationUpdateSchema } from "@homarr/validation/integration";
import { validation } from "@homarr/validation";
import { SecretCard } from "../../_components/secrets/integration-secret-card";
import { IntegrationSecretInput } from "../../_components/secrets/integration-secret-inputs";
import { IntegrationTestConnectionError } from "../../_components/test-connection/integration-test-connection-error";
import type { AnyMappedTestConnectionError } from "../../_components/test-connection/types";
interface EditIntegrationForm {
integration: RouterOutputs["integration"]["byId"];
@@ -34,7 +32,7 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
) ?? getDefaultSecretKinds(integration.kind);
const router = useRouter();
const form = useZodForm(integrationUpdateSchema.omit({ id: true }), {
const form = useZodForm(validation.integration.update.omit({ id: true }), {
initialValues: {
name: integration.name,
url: integration.url,
@@ -45,7 +43,6 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
},
});
const { mutateAsync, isPending } = clientApi.integration.update.useMutation();
const [error, setError] = useState<null | AnyMappedTestConnectionError>(null);
const secretsMap = new Map(integration.secrets.map((secret) => [secret.kind, secret]));
@@ -60,24 +57,26 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
})),
},
{
onSuccess: (data) => {
// We do it this way as we are unable to send a typesafe error through onError
if (data?.error) {
setError(data.error);
showErrorNotification({
title: t("integration.page.edit.notification.error.title"),
message: t("integration.page.edit.notification.error.message"),
});
return;
}
onSuccess: () => {
showSuccessNotification({
title: t("integration.page.edit.notification.success.title"),
message: t("integration.page.edit.notification.success.message"),
});
void revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations"));
},
onError: () => {
onError: (error) => {
const testConnectionError = convertIntegrationTestConnectionError(error.data?.error);
if (testConnectionError) {
showErrorNotification({
title: t(`integration.testConnection.notification.${testConnectionError.key}.title`),
message:
testConnectionError.message ??
t(`integration.testConnection.notification.${testConnectionError.key}.message`),
});
return;
}
showErrorNotification({
title: t("integration.page.edit.notification.error.title"),
message: t("integration.page.edit.notification.error.message"),
@@ -129,8 +128,6 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
</Stack>
</Fieldset>
{error !== null && <IntegrationTestConnectionError error={error} url={form.values.url} />}
<Group justify="end" align="center">
<Button variant="default" component={Link} href="/manage/integrations">
{t("common.action.backToOverview")}
@@ -144,4 +141,4 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
);
};
type FormType = Omit<z.infer<typeof integrationUpdateSchema>, "id">;
type FormType = Omit<z.infer<typeof validation.integration.update>, "id">;
@@ -1,51 +1,57 @@
import { notFound } from "next/navigation";
import { Container, Group, Stack, Title } from "@mantine/core";
import { z } from "zod/v4";
import { z } from "zod";
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 { integrationCreateSchema } from "@homarr/validation/integration";
import type { validation } from "@homarr/validation";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { NewIntegrationForm } from "./_integration-new-form";
import { NewIntegrationForm } from "../_integration-new-form";
interface NewIntegrationPageProps {
searchParams: Promise<
Partial<z.infer<typeof integrationCreateSchema>> & {
kind: IntegrationKind;
}
>;
interface NewIntegrationByIdPageProps {
params: {
id: string;
};
searchParams: Partial<z.infer<typeof validation.integration.create>>;
}
export default async function IntegrationsNewPage(props: NewIntegrationPageProps) {
const searchParams = await props.searchParams;
export function generateStaticParams() {
return integrationKinds.map((kind) => ({
id: kind,
}));
}
export default async function IntegrationNewByIdPage(props: NewIntegrationByIdPageProps) {
const { id } = props.params;
const session = await auth();
if (!session?.user.permissions.includes("integration-create")) {
notFound();
}
const result = z.enum(integrationKinds).safeParse(searchParams.kind);
const result = z.enum(integrationKinds).safeParse(id);
if (!result.success) {
notFound();
}
const tCreate = await getScopedI18n("integration.page.create");
const currentKind = result.data;
const dynamicMappings = new Map<string, string>([[id, getIntegrationName(currentKind)]]);
return (
<>
<DynamicBreadcrumb />
<DynamicBreadcrumb dynamicMappings={dynamicMappings} nonInteractable={["new"]} />
<Container>
<Stack>
<Group align="center">
<IntegrationAvatar kind={currentKind} size="md" />
<Title>{tCreate("title", { name: getIntegrationName(currentKind) })}</Title>
</Group>
<NewIntegrationForm searchParams={searchParams} />
<NewIntegrationForm searchParams={{ kind: currentKind }} />
</Stack>
</Container>
</>
@@ -10,20 +10,15 @@ import { getIntegrationName, integrationKinds } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import { IntegrationAvatar } from "@homarr/ui";
interface IntegrationCreateDropdownContentProps {
enableMockIntegration: boolean;
}
export const IntegrationCreateDropdownContent = ({ enableMockIntegration }: IntegrationCreateDropdownContentProps) => {
export const IntegrationCreateDropdownContent = () => {
const t = useI18n();
const [search, setSearch] = useState("");
const filteredKinds = useMemo(() => {
return integrationKinds
.filter((kind) => enableMockIntegration || kind !== "mock")
.filter((kind) => getIntegrationName(kind).toLowerCase().includes(search.toLowerCase().trim()))
.sort((kindA, kindB) => getIntegrationName(kindA).localeCompare(getIntegrationName(kindB)));
}, [search, enableMockIntegration]);
return integrationKinds.filter((kind) =>
getIntegrationName(kind).toLowerCase().includes(search.toLowerCase().trim()),
);
}, [search]);
const handleSearch = React.useCallback(
(event: ChangeEvent<HTMLInputElement>) => setSearch(event.target.value),
@@ -44,7 +39,7 @@ export const IntegrationCreateDropdownContent = ({ enableMockIntegration }: Inte
{filteredKinds.length > 0 ? (
<ScrollArea.Autosize mah={384}>
{filteredKinds.map((kind) => (
<Menu.Item component={Link} href={`/manage/integrations/new?kind=${kind}`} key={kind}>
<Menu.Item component={Link} href={`/manage/integrations/new/${kind}`} key={kind}>
<Group>
<IntegrationAvatar kind={kind} size="sm" />
<Text size="sm">{getIntegrationName(kind)}</Text>
@@ -16,39 +16,31 @@ import {
TextInput,
} from "@mantine/core";
import { IconInfoCircle } from "@tabler/icons-react";
import { z } from "zod/v4";
import { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import type { IntegrationKind, IntegrationSecretKind } from "@homarr/definitions";
import {
getAllSecretKindOptions,
getIconUrl,
getIntegrationDefaultUrl,
getIntegrationName,
integrationDefs,
} from "@homarr/definitions";
import { getAllSecretKindOptions, getIconUrl, getIntegrationName, integrationDefs } from "@homarr/definitions";
import type { UseFormReturnType } from "@homarr/form";
import { useZodForm } from "@homarr/form";
import { convertIntegrationTestConnectionError } from "@homarr/integrations/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { appHrefSchema } from "@homarr/validation/app";
import { integrationCreateSchema } from "@homarr/validation/integration";
import { validation } from "@homarr/validation";
import { IntegrationSecretInput } from "../_components/secrets/integration-secret-inputs";
import { IntegrationTestConnectionError } from "../_components/test-connection/integration-test-connection-error";
import type { AnyMappedTestConnectionError } from "../_components/test-connection/types";
interface NewIntegrationFormProps {
searchParams: Partial<z.infer<typeof integrationCreateSchema>> & {
searchParams: Partial<z.infer<typeof validation.integration.create>> & {
kind: IntegrationKind;
};
}
const formSchema = integrationCreateSchema.omit({ kind: true }).and(
const formSchema = validation.integration.create.omit({ kind: true }).and(
z.object({
createApp: z.boolean(),
appHref: appHrefSchema,
appHref: validation.app.manage.shape.href,
}),
);
@@ -60,7 +52,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
const form = useZodForm(formSchema, {
initialValues: {
name: searchParams.name ?? getIntegrationName(searchParams.kind),
url: searchParams.url ?? getIntegrationDefaultUrl(searchParams.kind) ?? "",
url: searchParams.url ?? "",
secrets: secretKinds[0].map((kind) => ({
kind,
value: "",
@@ -80,7 +72,6 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
clientApi.integration.create.useMutation();
const { mutateAsync: createAppAsync, isPending: isPendingApp } = clientApi.app.create.useMutation();
const isPending = isPendingIntegration || isPendingApp;
const [error, setError] = useState<null | AnyMappedTestConnectionError>(null);
const handleSubmitAsync = async (values: FormType) => {
await createIntegrationAsync(
@@ -89,17 +80,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
...values,
},
{
async onSuccess(data) {
// We do it this way as we are unable to send a typesafe error through onError
if (data?.error) {
setError(data.error);
showErrorNotification({
title: t("integration.page.create.notification.error.title"),
message: t("integration.page.create.notification.error.message"),
});
return;
}
async onSuccess() {
showSuccessNotification({
title: t("integration.page.create.notification.success.title"),
message: t("integration.page.create.notification.success.message"),
@@ -132,7 +113,19 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
},
);
},
onError: () => {
onError: (error) => {
const testConnectionError = convertIntegrationTestConnectionError(error.data?.error);
if (testConnectionError) {
showErrorNotification({
title: t(`integration.testConnection.notification.${testConnectionError.key}.title`),
message:
testConnectionError.message ??
t(`integration.testConnection.notification.${testConnectionError.key}.message`),
});
return;
}
showErrorNotification({
title: t("integration.page.create.notification.error.title"),
message: t("integration.page.create.notification.error.message"),
@@ -170,8 +163,6 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
</Stack>
</Fieldset>
{error !== null && <IntegrationTestConnectionError error={error} url={form.values.url} />}
{supportsSearchEngine && (
<Checkbox
label={t("integration.field.attemptSearchEngineCreation.label")}
@@ -41,7 +41,6 @@ import { CountBadge, IntegrationAvatar } from "@homarr/ui";
import { ManageContainer } from "~/components/manage/manage-container";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { NoResults } from "~/components/no-results";
import { env } from "~/env";
import { ActiveTabAccordion } from "../../../../components/active-tab-accordion";
import { DeleteIntegrationActionButton } from "./_integration-buttons";
import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown";
@@ -115,7 +114,7 @@ const IntegrationSelectMenu = ({ children }: PropsWithChildren) => {
>
{children}
<MenuDropdown>
<IntegrationCreateDropdownContent enableMockIntegration={env.UNSAFE_ENABLE_MOCK_INTEGRATION} />
<IntegrationCreateDropdownContent />
</MenuDropdown>
</Menu>
);
@@ -16,7 +16,7 @@ import {
Tooltip,
} from "@mantine/core";
import { IconExternalLink } from "@tabler/icons-react";
import { z } from "zod/v4";
import { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
@@ -4,7 +4,7 @@ import Link from "next/link";
import type { SegmentedControlItem } from "@mantine/core";
import { Button, Fieldset, Grid, Group, SegmentedControl, Stack, Textarea, TextInput } from "@mantine/core";
import { WidgetIntegrationSelect } from "node_modules/@homarr/widgets/src/widget-integration-select";
import type { z } from "zod/v4";
import type { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { searchEngineTypes } from "@homarr/definitions";
@@ -12,9 +12,9 @@ import { useZodForm } from "@homarr/form";
import { IconPicker } from "@homarr/forms-collection";
import type { TranslationFunction } from "@homarr/translation";
import { useI18n } from "@homarr/translation/client";
import { searchEngineManageSchema } from "@homarr/validation/search-engine";
import { validation } from "@homarr/validation";
type FormType = z.infer<typeof searchEngineManageSchema>;
type FormType = z.infer<typeof validation.searchEngine.manage>;
interface SearchEngineFormProps {
submitButtonTranslation: (t: TranslationFunction) => string;
@@ -30,7 +30,7 @@ export const SearchEngineForm = (props: SearchEngineFormProps) => {
const [integrationData] = clientApi.integration.allThatSupportSearch.useSuspenseQuery();
const form = useZodForm(searchEngineManageSchema, {
const form = useZodForm(validation.searchEngine.manage, {
initialValues: initialValues ?? {
name: "",
short: "",
@@ -2,7 +2,7 @@
import { useCallback } from "react";
import { useRouter } from "next/navigation";
import type { z } from "zod/v4";
import type { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -10,7 +10,7 @@ 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 { searchEngineManageSchema } from "@homarr/validation/search-engine";
import type { validation } from "@homarr/validation";
import { SearchEngineForm } from "../../_form";
@@ -41,7 +41,7 @@ export const SearchEngineEditForm = ({ searchEngine }: SearchEngineEditFormProps
});
const handleSubmit = useCallback(
(values: z.infer<typeof searchEngineManageSchema>) => {
(values: z.infer<typeof validation.searchEngine.manage>) => {
mutate({
id: searchEngine.id,
...values,
@@ -2,14 +2,14 @@
import { useCallback } from "react";
import { useRouter } from "next/navigation";
import type { z } from "zod/v4";
import type { z } from "zod";
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 { searchEngineManageSchema } from "@homarr/validation/search-engine";
import type { validation } from "@homarr/validation";
import { SearchEngineForm } from "../_form";
@@ -35,7 +35,7 @@ export const SearchEngineNewForm = () => {
});
const handleSubmit = useCallback(
(values: z.infer<typeof searchEngineManageSchema>) => {
(values: z.infer<typeof validation.searchEngine.manage>) => {
mutate(values);
},
[mutate],
@@ -2,7 +2,7 @@ import Link from "next/link";
import { redirect } from "next/navigation";
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
import { IconPencil, IconSearch } from "@tabler/icons-react";
import { z } from "zod/v4";
import { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
@@ -1,23 +1,82 @@
"use client";
import { Button } from "@mantine/core";
import { Button, FileInput, Group, Stack } from "@mantine/core";
import { IconCertificate } from "@tabler/icons-react";
import { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useModalAction } from "@homarr/modals";
import { AddCertificateModal } from "@homarr/modals-collection";
import { useZodForm } from "@homarr/form";
import { createModal, useModalAction } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { superRefineCertificateFile } from "@homarr/validation";
export const AddCertificateButton = () => {
const { openModal } = useModalAction(AddCertificateModal);
const t = useI18n();
const handleClick = () => {
openModal({
async onSuccess() {
await revalidatePathActionAsync("/manage/tools/certificates");
},
});
openModal({});
};
return <Button onClick={handleClick}>{t("certificate.action.create.label")}</Button>;
};
const AddCertificateModal = createModal(({ actions }) => {
const t = useI18n();
const form = useZodForm(
z.object({
file: z.instanceof(File).nullable().superRefine(superRefineCertificateFile),
}),
{
initialValues: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
file: null!,
},
},
);
const { mutateAsync } = clientApi.certificates.addCertificate.useMutation();
return (
<form
onSubmit={form.onSubmit(async (values) => {
const formData = new FormData();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
formData.set("file", values.file!);
await mutateAsync(formData, {
async onSuccess() {
showSuccessNotification({
title: t("certificate.action.create.notification.success.title"),
message: t("certificate.action.create.notification.success.message"),
});
await revalidatePathActionAsync("/manage/tools/certificates");
actions.closeModal();
},
onError() {
showErrorNotification({
title: t("certificate.action.create.notification.error.title"),
message: t("certificate.action.create.notification.error.message"),
});
},
});
})}
>
<Stack>
<FileInput leftSection={<IconCertificate size={16} />} {...form.getInputProps("file")} />
<Group justify="end">
<Button onClick={actions.closeModal} variant="subtle" color="gray">
{t("common.action.cancel")}
</Button>
<Button type="submit" loading={form.submitting}>
{t("common.action.add")}
</Button>
</Group>
</Stack>
</form>
);
}).withOptions({
defaultTitle(t) {
return t("certificate.action.create.label");
},
});
@@ -1,52 +0,0 @@
"use client";
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 { useI18n } from "@homarr/translation/client";
interface RemoveHostnameActionIconProps {
hostname: string;
thumbprint: string;
}
export const RemoveHostnameActionIcon = (input: RemoveHostnameActionIconProps) => {
const { mutateAsync } = clientApi.certificates.removeTrustedHostname.useMutation();
const { openConfirmModal } = useConfirmModal();
const t = useI18n();
const handleRemove = () => {
openConfirmModal({
title: t("certificate.action.removeHostname.label"),
children: t("certificate.action.removeHostname.confirm"),
// eslint-disable-next-line no-restricted-syntax
async onConfirm() {
await mutateAsync(input, {
async onSuccess() {
await revalidatePathActionAsync("/manage/tools/certificates/hostnames");
showSuccessNotification({
title: t("certificate.action.removeHostname.notification.success.title"),
message: t("certificate.action.removeHostname.notification.success.message"),
});
},
onError() {
showErrorNotification({
title: t("certificate.action.removeHostname.notification.error.title"),
message: t("certificate.action.removeHostname.notification.error.message"),
});
},
});
},
});
};
return (
<ActionIcon color="red" variant="subtle" onClick={handleRemove}>
<IconTrash color="red" size={16} stroke={1.5} />
</ActionIcon>
);
};
@@ -1,99 +0,0 @@
import { X509Certificate } from "node:crypto";
import Link from "next/link";
import { notFound } from "next/navigation";
import {
Button,
Group,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text,
Title,
} from "@mantine/core";
import { IconCertificateOff } from "@tabler/icons-react";
import { auth } from "@homarr/auth/next";
import { getTrustedCertificateHostnamesAsync } from "@homarr/certificates/server";
import { getI18n } from "@homarr/translation/server";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { NoResults } from "~/components/no-results";
import { RemoveHostnameActionIcon } from "./_components/remove-hostname";
export default async function TrustedHostnamesPage() {
const session = await auth();
if (!session?.user.permissions.includes("admin")) {
notFound();
}
const t = await getI18n();
const trustedHostnames = await getTrustedCertificateHostnamesAsync().then((hostnames) => {
return hostnames.map((hostname) => {
let subject: string | null;
try {
subject = new X509Certificate(hostname.certificate).subject;
} catch {
subject = null;
}
return {
...hostname,
subject,
};
});
});
return (
<>
<DynamicBreadcrumb />
<Stack>
<Group justify="space-between">
<Stack gap={4}>
<Title>{t("certificate.page.hostnames.title")}</Title>
<Text>{t("certificate.page.hostnames.description")}</Text>
</Stack>
<Button variant="default" component={Link} href="/manage/tools/certificates">
{t("certificate.page.hostnames.toCertificates")}
</Button>
</Group>
{trustedHostnames.length === 0 && (
<NoResults icon={IconCertificateOff} title={t("certificate.page.hostnames.noResults.title")} />
)}
{trustedHostnames.length >= 1 && (
<Table>
<TableThead>
<TableTr>
<TableTh>{t("certificate.field.hostname.label")}</TableTh>
<TableTh>{t("certificate.field.subject.label")}</TableTh>
<TableTh>{t("certificate.field.fingerprint.label")}</TableTh>
<TableTh></TableTh>
</TableTr>
</TableThead>
<TableTbody>
{trustedHostnames.map(({ hostname, subject, thumbprint }) => (
<TableTr key={`${hostname}-${thumbprint}`}>
<TableTd>{hostname}</TableTd>
<TableTd>{subject}</TableTd>
<TableTd>{thumbprint}</TableTd>
<TableTd>
<Group justify="end">
<RemoveHostnameActionIcon hostname={hostname} thumbprint={thumbprint} />
</Group>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
)}
</Stack>
</>
);
}
@@ -1,8 +1,7 @@
import { X509Certificate } from "node:crypto";
import Link from "next/link";
import { notFound } from "next/navigation";
import { Button, Card, Group, SimpleGrid, Stack, Text, Title } from "@mantine/core";
import { IconAlertTriangle, IconCertificate, IconCertificateOff } from "@tabler/icons-react";
import { Card, Group, SimpleGrid, Stack, Text, Title } from "@mantine/core";
import { IconCertificate, IconCertificateOff } from "@tabler/icons-react";
import dayjs from "dayjs";
import { auth } from "@homarr/auth/next";
@@ -32,27 +31,11 @@ export default async function CertificatesPage({ params }: CertificatesPageProps
const t = await getI18n();
const certificates = await loadCustomRootCertificatesAsync();
const x509Certificates = certificates
.map((cert) => {
try {
const x509 = new X509Certificate(cert.content);
return {
...cert,
isError: false,
x509,
} as const;
} catch {
return {
...cert,
isError: true,
x509: null,
} as const;
}
})
.sort((certA, certB) => {
if (certA.isError) return -1;
if (certB.isError) return 1;
return certA.x509.validToDate.getTime() - certB.x509.validToDate.getTime();
});
.map((cert) => ({
...cert,
x509: new X509Certificate(cert.content),
}))
.sort((certA, certB) => certA.x509.validToDate.getTime() - certB.x509.validToDate.getTime());
return (
<>
@@ -65,12 +48,7 @@ export default async function CertificatesPage({ params }: CertificatesPageProps
<Text>{t("certificate.page.list.description")}</Text>
</Stack>
<Group>
<Button variant="default" component={Link} href="/manage/tools/certificates/hostnames">
{t("certificate.page.list.toHostnames")}
</Button>
<AddCertificateButton />
</Group>
<AddCertificateButton />
</Group>
{x509Certificates.length === 0 && (
@@ -79,47 +57,32 @@ export default async function CertificatesPage({ params }: CertificatesPageProps
<SimpleGrid cols={{ sm: 1, lg: 2, xl: 3 }} spacing="lg">
{x509Certificates.map((cert) => (
<Card key={cert.fileName} withBorder>
<Card key={cert.x509.fingerprint} withBorder>
<Group wrap="nowrap">
{cert.isError ? (
<IconAlertTriangle
color={getMantineColor("red", 6)}
style={{ minWidth: 32 }}
size={32}
stroke={1.5}
/>
) : (
<IconCertificate
color={getMantineColor(iconColor(cert.x509.validToDate), 6)}
style={{ minWidth: 32 }}
size={32}
stroke={1.5}
/>
)}
<IconCertificate
color={getMantineColor(iconColor(cert.x509.validToDate), 6)}
style={{ minWidth: 32 }}
size={32}
stroke={1.5}
/>
<Stack flex={1} gap="xs" maw="calc(100% - 48px)">
<Group justify="space-between" wrap="nowrap">
<Text fw={500} lineClamp={1} style={{ wordBreak: "break-all" }}>
{cert.isError ? t("certificate.page.list.invalid.title") : cert.x509.subject}
{cert.x509.subject}
</Text>
<Text c="gray.6" ta="end" size="sm">
{cert.fileName}
</Text>
</Group>
<Group justify="space-between">
{cert.isError ? (
<Text size="sm" c="gray.6">
{t("certificate.page.list.invalid.description")}
</Text>
) : (
<Text size="sm" c="gray.6" title={cert.x509.validToDate.toISOString()}>
{t("certificate.page.list.expires", {
when: new Intl.RelativeTimeFormat(locale).format(
dayjs(cert.x509.validToDate).diff(dayjs(), "days"),
"days",
),
})}
</Text>
)}
<Text size="sm" c="gray.6" title={cert.x509.validToDate.toISOString()}>
{t("certificate.page.list.expires", {
when: new Intl.RelativeTimeFormat(locale).format(
dayjs(cert.x509.validToDate).diff(dayjs(), "days"),
"days",
),
})}
</Text>
<RemoveCertificate fileName={cert.fileName} />
</Group>
</Stack>
@@ -17,7 +17,6 @@ import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useTimeAgo } from "@homarr/common";
import type { ContainerState } from "@homarr/docker";
import { containerStateColorMap } from "@homarr/docker/shared";
import { useModalAction } from "@homarr/modals";
import { AddDockerAppToHomarr } from "@homarr/modals-collection";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
@@ -245,11 +244,21 @@ const ContainerActionBarButton = (props: ContainerActionBarButtonProps) => {
);
};
const containerStates = {
created: "cyan",
running: "green",
paused: "yellow",
restarting: "orange",
exited: "red",
removing: "pink",
dead: "dark",
} satisfies Record<ContainerState, MantineColor>;
const ContainerStateBadge = ({ state }: { state: ContainerState }) => {
const t = useScopedI18n("docker.field.state.option");
return (
<Badge size="lg" radius="sm" variant="light" w={120} color={containerStateColorMap[state]}>
<Badge size="lg" radius="sm" variant="light" w={120} color={containerStates[state]}>
{t(state)}
</Badge>
);
@@ -3,7 +3,7 @@
import { SimpleGrid, Skeleton, Stack, Title } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/common";
import { createId } from "@homarr/db/client";
import type { KubernetesLabelResourceType } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
@@ -10,7 +10,7 @@ import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/common";
import { createId } from "@homarr/db/client";
import type { KubernetesIngress } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
@@ -8,7 +8,7 @@ import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { createId } from "@homarr/common";
import { createId } from "@homarr/db/client";
import type { KubernetesService } from "@homarr/definitions";
import type { ScopedTranslationFunction } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
@@ -1,26 +0,0 @@
"use client";
import { Select } from "@mantine/core";
import type { LogLevel } from "@homarr/log/constants";
import { logLevelConfiguration, logLevels } from "@homarr/log/constants";
import { useI18n } from "@homarr/translation/client";
import { useLogContext } from "./log-context";
export const LogLevelSelection = () => {
const { level, setLevel } = useLogContext();
const t = useI18n();
return (
<Select
data={logLevels.map((level) => ({
value: level,
label: `${logLevelConfiguration[level].prefix} ${t(`log.level.option.${level}`)}`,
}))}
value={level}
onChange={(value) => setLevel(value as LogLevel)}
checkIconPosition="right"
/>
);
};
@@ -1,32 +0,0 @@
"use client";
import type { PropsWithChildren } from "react";
import { createContext, useContext, useMemo, useState } from "react";
import type { LogLevel } from "@homarr/log/constants";
import { logLevels } from "@homarr/log/constants";
const LogContext = createContext<{
level: LogLevel;
setLevel: (level: LogLevel) => void;
activeLevels: LogLevel[];
} | null>(null);
interface LogContextProviderProps extends PropsWithChildren {
defaultLevel: LogLevel;
}
export const LogContextProvider = ({ defaultLevel, children }: LogContextProviderProps) => {
const [level, setLevel] = useState(defaultLevel);
const activeLevels = useMemo(() => logLevels.slice(0, logLevels.indexOf(level) + 1), [level]);
return <LogContext.Provider value={{ level, setLevel, activeLevels }}>{children}</LogContext.Provider>;
};
export const useLogContext = () => {
const context = useContext(LogContext);
if (!context) {
throw new Error("useLogContext must be used within a LogContextProvider");
}
return context;
};
@@ -1,4 +1,4 @@
import { Box, Group } from "@mantine/core";
import { Box } from "@mantine/core";
import { getScopedI18n } from "@homarr/translation/server";
@@ -7,14 +7,11 @@ import "@xterm/xterm/css/xterm.css";
import { notFound } from "next/navigation";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/log/env";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
import { createMetaTitle } from "~/metadata";
import { ClientSideTerminalComponent } from "./client";
import { LogLevelSelection } from "./level-selection";
import { LogContextProvider } from "./log-context";
export async function generateMetadata() {
const session = await auth();
@@ -35,14 +32,11 @@ export default async function LogsManagementPage() {
}
return (
<LogContextProvider defaultLevel={env.LOG_LEVEL}>
<Group justify="space-between" align="center" wrap="nowrap">
<DynamicBreadcrumb />
<LogLevelSelection />
</Group>
<>
<DynamicBreadcrumb />
<Box style={{ borderRadius: 6 }} h={fullHeightWithoutHeaderAndFooter} p="md" bg="black">
<ClientSideTerminalComponent />
</Box>
</LogContextProvider>
</>
);
}
@@ -8,29 +8,22 @@ import { Terminal } from "@xterm/xterm";
import { clientApi } from "@homarr/api/client";
import { useLogContext } from "./log-context";
import classes from "./terminal.module.css";
export const TerminalComponent = () => {
const ref = useRef<HTMLDivElement>(null);
const { activeLevels } = useLogContext();
const terminalRef = useRef<Terminal>(null);
clientApi.log.subscribe.useSubscription(
{
levels: activeLevels,
clientApi.log.subscribe.useSubscription(undefined, {
onData(data) {
terminalRef.current?.writeln(`${data.timestamp} ${data.level} ${data.message}`);
terminalRef.current?.refresh(0, terminalRef.current.rows - 1);
},
{
onData(data) {
terminalRef.current?.writeln(data.message);
terminalRef.current?.refresh(0, terminalRef.current.rows - 1);
},
onError(err) {
// This makes sense as logging might cause an infinite loop
alert(err);
},
onError(err) {
// This makes sense as logging might cause an infinite loop
alert(err);
},
);
});
useEffect(() => {
if (!ref.current) {
@@ -0,0 +1,95 @@
"use client";
import React from "react";
import { ActionIcon, Badge, Card, Group, Stack, Text } from "@mantine/core";
import { useListState } from "@mantine/hooks";
import { IconPlayerPlay } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useTimeAgo } from "@homarr/common";
import type { TaskStatus } from "@homarr/cron-job-status";
import type { TranslationKeys } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
interface JobsListProps {
initialJobs: RouterOutputs["cronJobs"]["getJobs"];
}
interface JobState {
job: JobsListProps["initialJobs"][number];
status: TaskStatus | null;
}
export const JobsList = ({ initialJobs }: JobsListProps) => {
const t = useScopedI18n("management.page.tool.tasks");
const [jobs, handlers] = useListState<JobState>(
initialJobs.map((job) => ({
job,
status: null,
})),
);
clientApi.cronJobs.subscribeToStatusUpdates.useSubscription(undefined, {
onData: (data) => {
const jobByName = jobs.find((job) => job.job.name === data.name);
if (!jobByName) {
return;
}
handlers.applyWhere(
(job) => job.job.name === data.name,
(job) => ({ ...job, status: data }),
);
},
});
const { mutateAsync } = clientApi.cronJobs.triggerJob.useMutation();
const handleJobTrigger = React.useCallback(
async (job: JobState) => {
if (job.status?.status === "running") {
return;
}
await mutateAsync(job.job.name);
},
[mutateAsync],
);
return (
<Stack>
{jobs.map((job) => (
<Card key={job.job.name} withBorder>
<Group justify={"space-between"} gap={"md"}>
<Stack gap={0}>
<Group>
<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>}
</Group>
{job.status && <TimeAgo timestamp={job.status.lastExecutionTimestamp} />}
</Stack>
{!job.job.preventManualExecution && (
<ActionIcon
onClick={() => handleJobTrigger(job)}
disabled={job.status?.status === "running"}
variant={"default"}
size={"xl"}
radius={"xl"}
>
<IconPlayerPlay stroke={1.5} />
</ActionIcon>
)}
</Group>
</Card>
))}
</Stack>
);
};
const TimeAgo = ({ timestamp }: { timestamp: string }) => {
const timeAgo = useTimeAgo(new Date(timestamp));
return (
<Text size={"sm"} c={"dimmed"}>
{timeAgo}
</Text>
);
};
@@ -1,451 +0,0 @@
"use client";
import { ActionIcon, Badge, Button, Group, Select, Text } from "@mantine/core";
import { useMap } from "@mantine/hooks";
import { IconPlayerPlay, IconPower, IconRefresh } from "@tabler/icons-react";
import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useTimeAgo } from "@homarr/common";
import type { TaskStatus } from "@homarr/cron-job-status";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { ScopedTranslationFunction, TranslationFunction } from "@homarr/translation";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
import { IconPowerOff } from "@homarr/ui/icons";
const cronExpressions = [
{
value: "*/5 * * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.seconds", { interval: 5 }),
},
{
value: "*/10 * * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.seconds", { interval: 10 }),
},
{
value: "*/20 * * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.seconds", { interval: 20 }),
},
{
value: "*/30 * * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.seconds", { interval: 30 }),
},
{
value: "* * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.minutes", { interval: 1 }),
},
{
value: "*/5 * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.minutes", { interval: 5 }),
},
{
value: "*/10 * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.minutes", { interval: 10 }),
},
{
value: "*/15 * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.minutes", { interval: 15 }),
},
// Every hour
{
value: "0 * * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.hours", { interval: 1 }),
},
// Every two hours
{
value: "0 */2 * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.hours", { interval: 2 }),
},
// Every four hours
{
value: "0 */4 * * *",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.hours", { interval: 4 }),
},
// Every midnight
{
value: "0 0 * * */1",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.midnight"),
},
{
value: "0 0 * * 1",
label: (t: TranslationFunction) => t("management.page.tool.tasks.interval.weeklyMonday"),
},
] satisfies { value: string; label: (t: TranslationFunction) => string }[];
type JobData = RouterOutputs["cronJobs"]["getJobs"][number] & {
status?: TaskStatus | null;
lastExecutionTime?: string;
};
const createColumns = (
t: TranslationFunction,
tTasks: ScopedTranslationFunction<"management.page.tool.tasks">,
jobStatusMap: Map<string, TaskStatus | null>,
triggerMutation: ReturnType<typeof clientApi.cronJobs.triggerJob.useMutation>,
updateIntervalMutation: ReturnType<typeof clientApi.cronJobs.updateJobInterval.useMutation>,
enableMutation: ReturnType<typeof clientApi.cronJobs.enableJob.useMutation>,
disableMutation: ReturnType<typeof clientApi.cronJobs.disableJob.useMutation>,
loadingStates: Map<string, { toggle: boolean; trigger: boolean; interval: boolean }>,
): MRT_ColumnDef<JobData>[] => [
{
accessorKey: "name",
header: tTasks("field.name.label"),
Cell({ row }) {
const status = jobStatusMap.get(row.original.name);
return (
<Group gap="xs">
<Text fw={500}>{tTasks(`job.${row.original.name}.label`)}</Text>
<StatusBadge isEnabled={row.original.isEnabled} status={status ?? null} />
{status?.lastExecutionStatus === "error" && (
<Badge color="red" size="sm">
{tTasks("status.error")}
</Badge>
)}
</Group>
);
},
},
{
accessorKey: "cron",
header: tTasks("field.interval.label"),
size: 200,
Cell({ row }) {
const handleIntervalChange = (newCron: string | null) => {
if (!newCron || newCron === row.original.cron) return;
const currentStates = loadingStates.get(row.original.name) ?? {
toggle: false,
trigger: false,
interval: false,
};
loadingStates.set(row.original.name, {
...currentStates,
interval: true,
});
try {
updateIntervalMutation.mutate({
name: row.original.name,
cron: newCron,
});
} finally {
const updatedStates = loadingStates.get(row.original.name) ?? {
toggle: false,
trigger: false,
interval: false,
};
loadingStates.set(row.original.name, {
...updatedStates,
interval: false,
});
}
};
return (
<Select
value={row.original.cron}
onChange={handleIntervalChange}
data={cronExpressions.map(({ value, label }) => ({
value,
label: label(t),
}))}
size="sm"
disabled={loadingStates.get(row.original.name)?.interval ?? false}
style={{ minWidth: 180 }}
/>
);
},
},
{
accessorKey: "lastExecutionTime",
header: tTasks("field.lastExecution.label"),
size: 150,
Cell({ row }) {
const status = jobStatusMap.get(row.original.name);
if (!status?.lastExecutionTimestamp) {
return (
<Text size="sm" c="dimmed">
—
</Text>
);
}
return <TimeAgo timestamp={status.lastExecutionTimestamp} />;
},
},
{
id: "actions",
header: tTasks("field.actions.label"),
size: 120,
enableSorting: false,
Cell({ row }) {
const status = jobStatusMap.get(row.original.name);
const handleToggleEnabled = () => {
const currentStates = loadingStates.get(row.original.name) ?? {
toggle: false,
trigger: false,
interval: false,
};
loadingStates.set(row.original.name, {
...currentStates,
toggle: true,
});
try {
if (row.original.isEnabled) {
disableMutation.mutate(row.original.name);
} else {
enableMutation.mutate(row.original.name);
}
} finally {
const updatedStates = loadingStates.get(row.original.name) ?? {
toggle: false,
trigger: false,
interval: false,
};
loadingStates.set(row.original.name, {
...updatedStates,
toggle: false,
});
}
};
const handleTrigger = () => {
if (status?.status === "running") return;
const currentStates = loadingStates.get(row.original.name) ?? {
toggle: false,
trigger: false,
interval: false,
};
loadingStates.set(row.original.name, {
...currentStates,
trigger: true,
});
try {
triggerMutation.mutate(row.original.name);
} finally {
const updatedStates = loadingStates.get(row.original.name) ?? {
toggle: false,
trigger: false,
interval: false,
};
loadingStates.set(row.original.name, {
...updatedStates,
trigger: false,
});
}
};
return (
<Group gap="xs">
{!row.original.preventManualExecution && (
<ActionIcon
onClick={handleTrigger}
disabled={status?.status === "running"}
loading={loadingStates.get(row.original.name)?.trigger ?? false}
variant="light"
color="green"
size="md"
>
<IconPlayerPlay size={16} />
</ActionIcon>
)}
<ActionIcon
onClick={handleToggleEnabled}
loading={loadingStates.get(row.original.name)?.toggle ?? false}
variant="light"
color={row.original.isEnabled ? "green" : "gray"}
size="md"
>
{row.original.isEnabled ? <IconPower size={16} /> : <IconPowerOff size={16} />}
</ActionIcon>
</Group>
);
},
},
];
interface TasksTableProps {
initialJobs: RouterOutputs["cronJobs"]["getJobs"];
}
export const TasksTable = ({ initialJobs }: TasksTableProps) => {
const t = useI18n();
const tTasks = useScopedI18n("management.page.tool.tasks");
const { data: jobs } = clientApi.cronJobs.getJobs.useQuery(undefined, {
initialData: initialJobs,
refetchOnMount: false,
});
const jobStatusMap = useMap<string, TaskStatus | null>(initialJobs.map(({ name }) => [name, null] as const));
const loadingStates = useMap<string, { toggle: boolean; trigger: boolean; interval: boolean }>();
clientApi.cronJobs.subscribeToStatusUpdates.useSubscription(undefined, {
onData: (data) => {
jobStatusMap.set(data.name, data);
},
});
const triggerMutation = clientApi.cronJobs.triggerJob.useMutation({
onError() {
showErrorNotification({
title: t("common.error"),
message: tTasks("trigger.error.message"),
});
},
onSuccess() {
showSuccessNotification({
title: t("common.success"),
message: tTasks("trigger.success.message"),
});
},
});
const updateIntervalMutation = clientApi.cronJobs.updateJobInterval.useMutation({
onError() {
showErrorNotification({
title: t("common.error"),
message: tTasks("interval.update.error.message"),
});
},
onSuccess: async () => {
await utils.cronJobs.getJobs.invalidate();
showSuccessNotification({
title: t("common.success"),
message: tTasks("interval.update.success.message"),
});
},
});
const enableMutation = clientApi.cronJobs.enableJob.useMutation({
onError() {
showErrorNotification({
title: t("common.error"),
message: tTasks("toggle.error.message"),
});
},
onSuccess: async () => {
await utils.cronJobs.getJobs.invalidate();
showSuccessNotification({
title: t("common.success"),
message: tTasks("enable.success.message"),
});
},
});
const disableMutation = clientApi.cronJobs.disableJob.useMutation({
onError() {
showErrorNotification({
title: t("common.error"),
message: tTasks("toggle.error.message"),
});
},
onSuccess: async () => {
await utils.cronJobs.getJobs.invalidate();
showSuccessNotification({
title: t("common.success"),
message: tTasks("disable.success.message"),
});
},
});
// Utils for refresh functionality
const utils = clientApi.useUtils();
const handleRefreshAsync = async () => {
try {
await utils.cronJobs.getJobs.invalidate();
showSuccessNotification({
title: t("common.success"),
message: tTasks("refresh.success.message"),
});
} catch {
showErrorNotification({
title: t("common.error"),
message: tTasks("refresh.error.message"),
});
}
};
const table = useTranslatedMantineReactTable({
data: jobs,
enableDensityToggle: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableRowSelection: false,
enableTableFooter: false,
enableBottomToolbar: false,
enableRowActions: false,
enableColumnOrdering: false,
enableSorting: false,
enableSortingRemoval: false,
positionGlobalFilter: "right",
mantineSearchTextInputProps: {
placeholder: tTasks("table.search", { count: String(jobs.length) }),
style: { minWidth: 300 },
},
initialState: { density: "xs", showGlobalFilter: true },
renderTopToolbarCustomActions: () => (
<Button variant="default" rightSection={<IconRefresh size="1rem" />} onClick={handleRefreshAsync}>
{tTasks("action.refresh.label")}
</Button>
),
columns: createColumns(
t,
tTasks,
jobStatusMap,
triggerMutation,
updateIntervalMutation,
enableMutation,
disableMutation,
loadingStates,
),
});
return <MantineReactTable table={table} />;
};
interface StatusBadgeProps {
isEnabled: boolean;
status: TaskStatus | null;
}
const StatusBadge = ({ isEnabled, status }: StatusBadgeProps) => {
const tTasks = useScopedI18n("management.page.tool.tasks");
if (!isEnabled) {
return (
<Badge color="yellow" size="sm">
{tTasks("status.disabled")}
</Badge>
);
}
if (!status) return null;
if (status.status === "running") {
return (
<Badge color="green" size="sm">
{tTasks("status.running")}
</Badge>
);
}
return (
<Badge variant="default" size="sm">
{tTasks("status.idle")}
</Badge>
);
};
const TimeAgo = ({ timestamp }: { timestamp: string }) => {
const timeAgo = useTimeAgo(new Date(timestamp));
return (
<Text size="sm" c="dimmed">
{timeAgo}
</Text>
);
};
@@ -1,13 +1,12 @@
import { notFound } from "next/navigation";
import { Stack, Title } from "@mantine/core";
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 { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { createMetaTitle } from "~/metadata";
import { TasksTable } from "./_components/tasks-table";
import { JobsList } from "./_components/jobs-list";
export async function generateMetadata() {
const session = await auth();
@@ -28,15 +27,10 @@ export default async function TasksPage() {
}
const jobs = await api.cronJobs.getJobs();
const tTasks = await getScopedI18n("management.page.tool.tasks");
return (
<>
<DynamicBreadcrumb />
<Stack>
<Title order={1}>{tTasks("title")}</Title>
<TasksTable initialJobs={jobs} />
</Stack>
</>
<Box>
<Title mb={"md"}>Tasks</Title>
<JobsList initialJobs={jobs} />
</Box>
);
}
@@ -1,7 +1,7 @@
"use client";
import { Button, Group, Stack } from "@mantine/core";
import type { z } from "zod/v4";
import type { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -9,7 +9,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { userChangeHomeBoardsSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
import type { Board } from "~/app/[locale]/boards/_types";
import { BoardSelect } from "~/components/board/board-select";
@@ -40,7 +40,7 @@ export const ChangeHomeBoardForm = ({ user, boardsData }: ChangeHomeBoardFormPro
});
},
});
const form = useZodForm(userChangeHomeBoardsSchema, {
const form = useZodForm(validation.user.changeHomeBoards, {
initialValues: {
homeBoardId: user.homeBoardId,
mobileHomeBoardId: user.mobileHomeBoardId,
@@ -73,7 +73,7 @@ export const ChangeHomeBoardForm = ({ user, boardsData }: ChangeHomeBoardFormPro
/>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
@@ -82,4 +82,4 @@ export const ChangeHomeBoardForm = ({ user, boardsData }: ChangeHomeBoardFormPro
);
};
type FormType = z.infer<typeof userChangeHomeBoardsSchema>;
type FormType = z.infer<typeof validation.user.changeHomeBoards>;
@@ -1,7 +1,7 @@
"use client";
import { Button, Group, Select, Stack, Switch } from "@mantine/core";
import type { z } from "zod/v4";
import type { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -9,7 +9,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { userChangeSearchPreferencesSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
interface ChangeSearchPreferencesFormProps {
user: RouterOutputs["user"]["getById"];
@@ -37,7 +37,7 @@ export const ChangeSearchPreferencesForm = ({ user, searchEnginesData }: ChangeS
});
},
});
const form = useZodForm(userChangeSearchPreferencesSchema, {
const form = useZodForm(validation.user.changeSearchPreferences, {
initialValues: {
defaultSearchEngineId: user.defaultSearchEngineId,
openInNewTab: user.openSearchInNewTab,
@@ -66,7 +66,7 @@ export const ChangeSearchPreferencesForm = ({ user, searchEnginesData }: ChangeS
/>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
@@ -75,4 +75,4 @@ export const ChangeSearchPreferencesForm = ({ user, searchEnginesData }: ChangeS
);
};
type FormType = z.infer<typeof userChangeSearchPreferencesSchema>;
type FormType = z.infer<typeof validation.user.changeSearchPreferences>;
@@ -4,7 +4,7 @@ import { Button, Group, Radio, Stack } from "@mantine/core";
import type { DayOfWeek } from "@mantine/dates";
import dayjs from "dayjs";
import localeData from "dayjs/plugin/localeData";
import type { z } from "zod/v4";
import type { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -12,7 +12,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { userFirstDayOfWeekSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
dayjs.extend(localeData);
@@ -42,7 +42,7 @@ export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
});
},
});
const form = useZodForm(userFirstDayOfWeekSchema, {
const form = useZodForm(validation.user.firstDayOfWeek, {
initialValues: {
firstDayOfWeek: user.firstDayOfWeek as DayOfWeek,
},
@@ -71,7 +71,7 @@ export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
</Radio.Group>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
@@ -80,4 +80,4 @@ export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
);
};
type FormType = z.infer<typeof userFirstDayOfWeekSchema>;
type FormType = z.infer<typeof validation.user.firstDayOfWeek>;
@@ -1,7 +1,7 @@
"use client";
import { Button, Group, Stack, Switch } from "@mantine/core";
import type { z } from "zod/v4";
import type { z } from "zod";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -9,7 +9,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { userPingIconsEnabledSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
interface PingIconsEnabledProps {
user: RouterOutputs["user"]["getById"];
@@ -35,7 +35,7 @@ export const PingIconsEnabled = ({ user }: PingIconsEnabledProps) => {
});
},
});
const form = useZodForm(userPingIconsEnabledSchema, {
const form = useZodForm(validation.user.pingIconsEnabled, {
initialValues: {
pingIconsEnabled: user.pingIconsEnabled,
},
@@ -57,7 +57,7 @@ export const PingIconsEnabled = ({ user }: PingIconsEnabledProps) => {
/>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
@@ -66,4 +66,4 @@ export const PingIconsEnabled = ({ user }: PingIconsEnabledProps) => {
);
};
type FormType = z.infer<typeof userPingIconsEnabledSchema>;
type FormType = z.infer<typeof validation.user.pingIconsEnabled>;
@@ -9,7 +9,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { userEditProfileSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
interface UserProfileFormProps {
user: RouterOutputs["user"]["getById"];
@@ -43,7 +43,7 @@ export const UserProfileForm = ({ user }: UserProfileFormProps) => {
});
},
});
const form = useZodForm(userEditProfileSchema.omit({ id: true }), {
const form = useZodForm(validation.user.editProfile.omit({ id: true }), {
initialValues: {
name: user.name ?? "",
email: user.email ?? "",
@@ -81,7 +81,7 @@ export const UserProfileForm = ({ user }: UserProfileFormProps) => {
{isProviderCredentials && (
<Group justify="end">
<Button type="submit" disabled={!form.isDirty()} loading={isPending}>
<Button type="submit" color="teal" disabled={!form.isDirty()} loading={isPending}>
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -10,7 +10,7 @@ import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { CustomPasswordInput } from "@homarr/ui";
import { userChangePasswordSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
interface ChangePasswordFormProps {
user: RouterOutputs["user"]["getById"];
@@ -34,7 +34,7 @@ export const ChangePasswordForm = ({ user }: ChangePasswordFormProps) => {
});
},
});
const form = useZodForm(userChangePasswordSchema, {
const form = useZodForm(validation.user.changePassword, {
initialValues: {
/* Require previous password if the current user want's to change his password */
previousPassword: session?.user.id === user.id ? "" : "_",
@@ -85,7 +85,7 @@ export const ChangePasswordForm = ({ user }: ChangePasswordFormProps) => {
/>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.confirm")}
</Button>
</Group>
@@ -17,7 +17,7 @@ import {
} from "@mantine/core";
import { useListState } from "@mantine/hooks";
import { IconPlus, IconUserCheck } from "@tabler/icons-react";
import { z } from "zod/v4";
import { z } from "zod";
import { clientApi } from "@homarr/api/client";
import { everyoneGroup, groupPermissions } from "@homarr/definitions";
@@ -27,8 +27,8 @@ import { useModalAction } from "@homarr/modals";
import { showErrorNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { CustomPasswordInput, UserAvatar } from "@homarr/ui";
import { createCustomErrorParams } from "@homarr/validation/form/i18n";
import { userPasswordSchema } from "@homarr/validation/user";
import { validation } from "@homarr/validation";
import { createCustomErrorParams } from "@homarr/validation/form";
import { GroupSelectModal } from "~/components/access/group-select-modal";
import { StepperNavigationComponent } from "./stepper-navigation";
@@ -84,7 +84,7 @@ export const UserCreateStepperComponent = ({ initialGroups }: UserCreateStepperC
const securityForm = useZodForm(
z
.object({
password: userPasswordSchema,
password: validation.user.password,
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
@@ -8,7 +8,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { groupUpdateSchema } from "@homarr/validation/group";
import { validation } from "@homarr/validation";
interface RenameGroupFormProps {
group: {
@@ -21,7 +21,7 @@ interface RenameGroupFormProps {
export const RenameGroupForm = ({ group, disabled }: RenameGroupFormProps) => {
const t = useI18n();
const { mutate, isPending } = clientApi.group.updateGroup.useMutation();
const form = useZodForm(groupUpdateSchema.pick({ name: true }), {
const form = useZodForm(validation.group.update.pick({ name: true }), {
initialValues: {
name: group.name,
},
@@ -68,7 +68,7 @@ export const RenameGroupForm = ({ group, disabled }: RenameGroupFormProps) => {
{!disabled && (
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -40,5 +40,9 @@ export const AddGroupMember = ({ groupId, presentUserIds }: AddGroupMemberProps)
);
}, [openModal, presentUserIds, groupId, mutateAsync, tMembersAdd]);
return <MobileAffixButton onClick={handleAddMember}>{tMembersAdd("label")}</MobileAffixButton>;
return (
<MobileAffixButton color="teal" onClick={handleAddMember}>
{tMembersAdd("label")}
</MobileAffixButton>
);
};
@@ -1,7 +1,7 @@
"use client";
import type { PropsWithChildren } from "react";
import { useCallback } from "react";
import type { PropsWithChildren } from "react";
import { Button, Card, Group, Switch, Text, Transition } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
@@ -108,7 +108,7 @@ export const SaveAffix = ({ groupId }: SaveAffixProps) => {
<Button disabled={isPending} onClick={form.reset}>
{t("common.action.discard")}
</Button>
<Button loading={isPending} onClick={handleSubmit}>
<Button color="teal" loading={isPending} onClick={handleSubmit}>
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -6,7 +6,7 @@ import { clientApi } from "@homarr/api/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
import { groupSettingsSchema } from "@homarr/validation/group";
import { validation } from "@homarr/validation";
import { BoardSelect } from "~/components/board/board-select";
@@ -19,7 +19,7 @@ interface GroupHomeBoardsProps {
export const GroupHomeBoards = ({ homeBoardId, mobileHomeBoardId, groupId }: GroupHomeBoardsProps) => {
const t = useI18n();
const [availableBoards] = clientApi.board.getBoardsForGroup.useSuspenseQuery({ groupId });
const form = useZodForm(groupSettingsSchema.pick({ homeBoardId: true, mobileHomeBoardId: true }), {
const form = useZodForm(validation.group.settings.pick({ homeBoardId: true, mobileHomeBoardId: true }), {
initialValues: {
homeBoardId,
mobileHomeBoardId,
@@ -71,7 +71,7 @@ export const GroupHomeBoards = ({ homeBoardId, mobileHomeBoardId, groupId }: Gro
/>
<Group justify="end">
<Button type="submit" loading={isPending}>
<Button type="submit" color="teal" loading={isPending}>
{t("common.action.save")}
</Button>
</Group>
@@ -57,5 +57,9 @@ const AddGroup = () => {
openModal();
}, [openModal]);
return <MobileAffixButton onClick={handleAddGroup}>{t("group.action.create.label")}</MobileAffixButton>;
return (
<MobileAffixButton onClick={handleAddGroup} color="teal">
{t("group.action.create.label")}
</MobileAffixButton>
);
};
@@ -264,7 +264,7 @@ const SaveAffix = ({ visible, isPending, onDiscard, onSave }: SaveAffixProps) =>
<Button disabled={isPending} onClick={onDiscard}>
{t("common.action.discard")}
</Button>
<Button loading={isPending} onClick={onSave}>
<Button color="teal" loading={isPending} onClick={onSave}>
{t("common.action.saveChanges")}
</Button>
</Group>
@@ -89,7 +89,11 @@ const RenderTopToolbarCustomActions = () => {
openModal();
}, [openModal]);
return <Button onClick={handleNewInvite}>{t("action.new.title")}</Button>;
return (
<Button color="teal" onClick={handleNewInvite}>
{t("action.new.title")}
</Button>
);
};
const RenderRowActions = ({ row }: { row: MRT_Row<RouterOutputs["invite"]["getAll"][number]> }) => {
@@ -11,7 +11,7 @@ import { useModalAction } from "@homarr/modals";
import { showSuccessNotification } from "@homarr/notifications";
import { useSettings } from "@homarr/settings";
import { useScopedI18n } from "@homarr/translation/client";
import type { BoardItemAdvancedOptions } from "@homarr/validation/shared";
import type { BoardItemAdvancedOptions } from "@homarr/validation";
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, widgetImports } from "@homarr/widgets";
import { WidgetError } from "@homarr/widgets/errors";
import { WidgetEditModal } from "@homarr/widgets/modals";
@@ -48,7 +48,6 @@ export const WidgetPreviewPageContent = ({ kind, integrationData }: WidgetPrevie
options: reduceWidgetOptionsWithDefaultValues(kind, settings, {}),
integrationIds: [],
advancedOptions: {
title: null,
customCssClasses: [],
borderColor: "",
},
@@ -1,19 +0,0 @@
import { notFound } from "next/navigation";
import { ImageProxy } from "@homarr/image-proxy";
export const GET = async (_request: Request, props: { params: Promise<{ id: string }> }) => {
const { id } = await props.params;
const imageProxy = new ImageProxy();
const image = await imageProxy.forwardImageAsync(id);
if (!image) {
notFound();
}
return new Response(image, {
headers: {
"Cache-Control": "public, max-age=3600, immutable", // Cache for 1 hour
},
});
};
@@ -23,7 +23,7 @@ export async function GET(_req: NextRequest, props: { params: Promise<{ id: stri
headers.set("Content-Type", image.contentType);
headers.set("Content-Length", image.content.length.toString());
return new NextResponse(new Uint8Array(image.content), {
return new NextResponse(image.content, {
status: 200,
headers,
});
@@ -82,7 +82,7 @@ export const GroupAccessForm = <TPermission extends string>({
<Button rightSection={<IconPlus size="1rem" />} variant="light" onClick={handleAddUser}>
{t("common.action.add")}
</Button>
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("permission.action.saveGroup")}
</Button>
</Group>
@@ -104,7 +104,7 @@ export const UsersAccessForm = <TPermission extends string>({
<Button rightSection={<IconPlus size="1rem" />} variant="light" onClick={handleAddUser}>
{t("common.action.add")}
</Button>
<Button type="submit" loading={isPending}>
<Button type="submit" loading={isPending} color="teal">
{t("permission.action.saveUser")}
</Button>
</Group>
@@ -1,6 +1,6 @@
import { getBoardLayouts } from "@homarr/boards/context";
import { createId } from "@homarr/common";
import type { Modify } from "@homarr/common/types";
import { createId } from "@homarr/db/client";
import type { WidgetKind } from "@homarr/definitions";
import type { Board, EmptySection, Item, ItemLayout } from "~/app/[locale]/boards/_types";
@@ -9,11 +9,10 @@ import { getSectionElements } from "./section-elements";
export interface CreateItemInput {
kind: WidgetKind;
options?: Record<string, unknown>;
}
export const createItemCallback =
({ kind, options = {} }: CreateItemInput) =>
({ kind }: CreateItemInput) =>
(previous: Board): Board => {
const firstSection = previous.sections
.filter((section): section is EmptySection => section.kind === "empty")
@@ -25,11 +24,10 @@ export const createItemCallback =
const widget = {
id: createId(),
kind,
options,
options: {},
layouts: createItemLayouts(previous, firstSection),
integrationIds: [],
advancedOptions: {
title: null,
customCssClasses: [],
borderColor: "",
},
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db/client";
import type { Board, EmptySection, ItemLayout, Section } from "~/app/[locale]/boards/_types";
import { getFirstEmptyPosition } from "./empty-position";
@@ -20,7 +20,7 @@ describe("item actions duplicate-item", () => {
kind: itemKind,
integrationIds: ["1"],
options: { address: "localhost" },
advancedOptions: { title: "The best one", customCssClasses: ["test"], borderColor: "#ff0000" },
advancedOptions: { customCssClasses: ["test"], borderColor: "#ff0000" },
})
.addLayout({ layoutId, sectionId: currentSectionId, ...currentItemSize })
.build();
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db";
import type { Board, DynamicSection, EmptySection, Item, Section } from "~/app/[locale]/boards/_types";
import { DynamicSectionMockBuilder } from "./dynamic-section-mock";
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db";
import type { CategorySection } from "~/app/[locale]/boards/_types";
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db";
import type { DynamicSection } from "~/app/[locale]/boards/_types";
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db";
import type { EmptySection } from "~/app/[locale]/boards/_types";
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db";
import type { Item } from "~/app/[locale]/boards/_types";
@@ -13,7 +13,6 @@ export class ItemMockBuilder {
layouts: [],
integrationIds: [],
advancedOptions: {
title: null,
customCssClasses: [],
borderColor: "",
},
@@ -1,4 +1,4 @@
import { createId } from "@homarr/common";
import { createId } from "@homarr/db";
import type { Board } from "~/app/[locale]/boards/_types";
@@ -1,7 +1,7 @@
import { useCallback } from "react";
import { useUpdateBoard } from "@homarr/boards/updater";
import type { BoardItemAdvancedOptions } from "@homarr/validation/shared";
import type { BoardItemAdvancedOptions } from "@homarr/validation";
import type { CreateItemInput } from "./actions/create-item";
import { createItemCallback } from "./actions/create-item";
Loaded 100 of 727 files, more files were not shown because too many files have changed in this diff. Show more