Compare commits

..
45 Commits
Author SHA1 Message Date
Releases Homarr b0e0b1d43f chore(release): 1.46.0 [skip ci] 2025-12-12 19:17:52 +00:00
homarr-releases[bot] cc403b89eb chore(release): automatic release v1.46.0 2025-12-12 19:16:55 +00:00
Meier Lukas dd24925d90 feat(docker): add widget options to sort containers (#4636) 2025-12-12 19:59:28 +01:00
ManuelandMeier Lukas 984b29d951 ci: publish build archives to release for bare metal installs (#4535)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-12 19:51:19 +01:00
Meier Lukas 2b2c0e7e3a fix(deps): update next monorepo to v16.0.10 [security] (#4634) 2025-12-12 15:44:35 +01:00
homarr-update-contributors[bot] e7b3be2edb chore: update contributors (#4633)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2025-12-12 12:38:42 +00:00
homarr-renovate[bot] 3b1c564a5a fix(deps): update dependency winston to v3.19.0 (#4632)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-12 08:39:42 +00:00
homarr-renovate[bot] e37325f5e0 fix(deps): update dependency ldapts to v8.0.14 (#4627)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-11 21:31:12 +00:00
homarr-renovate[bot] 8a14a6cb45 fix(deps): update dependency ldapts to v8.0.13 (#4626)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-11 00:37:25 +01:00
homarr-renovate[bot]andMeier Lukas d111eee0e0 fix(deps): update tiptap monorepo to v3 (major) (#3612)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-11 00:35:55 +01:00
Meier Lukas 0b4ca93aa1 fix(transmission): total download incorrect for cross seeded torrents (#3211) 2025-12-10 20:50:39 +01:00
Meier Lukas 6a908397cd fix(plex): test connection not fully checking connection (#4617) 2025-12-10 13:28:28 +01:00
homarr-renovate[bot] 85689caf6c fix(deps): update turbo monorepo to ^2.6.3 (#4622)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-09 18:13:32 +00:00
homarr-renovate[bot]andMeier Lukas cab5d19141 fix(deps): update dependency drizzle-orm to ^0.45.0 (#4619)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-09 19:07:54 +01:00
homarr-renovate[bot] 5cbab28099 fix(deps): update dependency drizzle-kit to ^0.31.8 (#4618)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-09 16:39:05 +01:00
Meier Lukas 1dd9694719 fix: format issues 2025-12-09 16:34:54 +01:00
Copilotandajnart 681aee05f0 fix: add autocomplete and id attributes for password manager compatibility (#4602)
Co-authored-by: ajnart <49837342+ajnart@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-12-09 15:59:33 +01:00
homarr-renovate[bot] c3ed7090c2 chore(deps): update dependency esbuild@&lt;&#x3d;0.24.2 to >=0.27.1 (#4611)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-09 15:51:42 +01:00
homarr-renovate[bot]andMeier Lukas a20248d0b6 fix(deps): update dependency better-sqlite3 to ^12.5.0 (#4569)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-09 15:51:11 +01:00
homarr-crowdin[bot] 5a95cd3753 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-12-09 14:49:00 +00:00
homarr-renovate[bot] efc0384b37 fix(deps): update dependency @tanstack/react-query to ^5.90.12 (#4615)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-09 14:48:34 +00:00
homarr-renovate[bot] fb04875253 chore(deps): update dependency esbuild to ^0.27.1 (#4610)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-09 15:46:59 +01:00
homarr-renovate[bot] 4689ec9933 fix(deps): update dependency ldapts to v8.0.12 (#4613)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-09 05:29:10 +00:00
TobiasandMeier Lukas d084dfb470 fix: improve run script (#4609)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-08 22:59:59 +01:00
homarr-renovate[bot] 7de45e6fe5 fix(deps): update turbo monorepo to ^2.6.2 (#4612)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-08 21:26:08 +00:00
homarr-renovate[bot] f6d16fe44b fix(deps): update react monorepo to v19.2.1 (#4608)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-08 16:36:42 +00:00
Releases Homarr 340f5606a9 chore(release): 1.45.3 [skip ci] 2025-12-08 16:11:13 +00:00
homarr-releases[bot] ca9478dce7 chore(release): automatic release v1.45.3 2025-12-08 16:10:20 +00:00
Meier Lukas 1bb3654bf7 Merge commit from fork 2025-12-08 16:54:15 +01:00
homarr-renovate[bot]andMeier Lukas d88c6027fe fix(deps): update dependency typescript-eslint to ^8.48.1 (#4438)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-08 16:52:25 +01:00
homarr-renovate[bot] 020cbd72d3 fix(deps): update dependency glob to v13 (#4584)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-08 15:50:54 +01:00
homarr-renovate[bot] aed2025b30 chore(deps): update vitest monorepo to ^4.0.15 (#4603)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-08 13:24:28 +00:00
homarr-renovate[bot]andMeier Lukas 54809823e9 fix(deps): update dependency prettier to ^3.7.4 (#4582)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-12-08 14:19:46 +01:00
homarr-renovate[bot] 4f92e5265a fix(deps): update dependency next-intl to v4.5.8 (#4606)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-08 11:38:56 +00:00
homarr-renovate[bot] 234266ebd9 chore(deps): update actions/checkout action to v6 (#4601)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-07 17:36:30 +01:00
homarr-renovate[bot] 6b28aa86d0 fix(deps): update dependency next-intl to v4.5.7 (#4598)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-07 01:59:48 +00:00
homarr-renovate[bot] ed638d4767 fix(deps): update dependency jotai to ^2.15.2 (#4597)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-07 01:59:14 +00:00
homarr-renovate[bot] a2f7d63d2a chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=7.2.6 (#4595)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-07 01:58:21 +00:00
homarr-crowdin[bot] 9639f44560 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-12-07 00:59:37 +00:00
Releases Homarr bc087e6e60 chore(release): 1.45.2 [skip ci] 2025-12-05 19:17:05 +00:00
homarr-releases[bot] d347d4455b chore(release): automatic release v1.45.2 2025-12-05 19:16:11 +00:00
Meier Lukas 10572ca962 fix: disable react-compiler due to issue with mantine-react-table data updates (#4593) 2025-12-05 19:10:23 +01:00
homarr-update-contributors[bot] d350e48998 chore: update contributors (#4591)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2025-12-05 14:56:10 +00:00
DZ945brH 2c48b18968 fix: add ipv6 support for internal nginx proxy 2025-12-05 13:04:17 +01:00
Meier Lukas 069ed0a267 chore: update bug-report template 2025-12-03 23:42:06 +01:00
81 changed files with 1707 additions and 1446 deletions

No files matched your search

+1
View File
@@ -33,6 +33,7 @@ body:
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
- 1.45.1
- 1.45.0
- 1.44.0
- 1.43.1
@@ -0,0 +1,43 @@
name: Extract Build Artifact
description: Extracts artifacts from an existing Docker image to be used for from source installation
inputs:
digest:
description: Digest of Docker image to use
required: true
architecture:
description: Name of architecture, will be used to create directories (e.g. amd64, arm64)
required: true
release-tag:
description: Tag of the release to which the artifact will be attached
required: true
repository:
description: Repository to which the release belongs, e.g. owner/repo
required: true
token:
description: GitHub token with permissions to upload release assets
required: true
runs:
using: "composite"
steps:
- name: Start docker container for ${{ inputs.architecture }}
run: |
docker run --name homarr \
-e "SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000" \
--detach --rm ${{ inputs.digest }}
shell: bash
- name: Extract build from ${{ inputs.architecture }} container
run: |
docker exec homarr cp /etc/nginx/templates/nginx.conf /app && \
docker exec homarr tar -czf extraction.tar.gz -C /app . && \
mkdir -p ${{ runner.temp }}/extraction/${{ inputs.architecture }} && \
docker cp homarr:/app/extraction.tar.gz ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-${{ inputs.architecture }}.tar.gz
shell: bash
- name: Stop ${{ inputs.architecture }} container
if: always()
run: docker container remove --force --volumes homarr
shell: bash
- name: Add build archive to release
env:
GH_TOKEN: ${{ inputs.token }}
run: gh release upload --repo ${{ inputs.repository }} ${{ inputs.release-tag }} ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-${{ inputs.architecture }}.tar.gz --clobber
shell: bash
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
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
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
+6 -6
View File
@@ -24,7 +24,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup
uses: ./tooling/github/setup
@@ -39,7 +39,7 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup
uses: ./tooling/github/setup
@@ -50,7 +50,7 @@ jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup
uses: ./tooling/github/setup
@@ -61,7 +61,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup
uses: ./tooling/github/setup
@@ -79,7 +79,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup
uses: ./tooling/github/setup
- name: Build docker image
@@ -102,7 +102,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup
uses: ./tooling/github/setup
- name: Copy env
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Obtain token
id: obtainToken
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Upload Crowdin translations
uses: crowdin/github-action@v2
+40 -6
View File
@@ -37,7 +37,7 @@ jobs:
outputs:
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
skipped: ${{ env.SKIP_RELEASE }}
steps:
- run: echo "Skipping release for workflow_dispatch event"
if: env.SKIP_RELEASE == 'true'
@@ -55,7 +55,7 @@ jobs:
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
permission-contents: write # required to commit package.json & changelog changes, merge them to dev and publish the release
- uses: actions/checkout@v5
- uses: actions/checkout@v6
if: env.SKIP_RELEASE == 'false'
with:
persist-credentials: false
@@ -115,7 +115,7 @@ jobs:
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{ needs.release.outputs.git_ref }}
@@ -146,7 +146,6 @@ jobs:
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
env:
SKIP_ENV_VALIDATION: true
build-arm64:
name: Build docker image for arm64
needs: release
@@ -154,7 +153,7 @@ jobs:
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{ needs.release.outputs.git_ref }}
@@ -185,10 +184,38 @@ jobs:
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
env:
SKIP_ENV_VALIDATION: true
extract-asset-amd64:
name: Extract amd64 asset from docker image
needs: [release, build-amd64]
runs-on: ubuntu-latest
steps:
- name: Extract amd64
if: needs.release.outputs.skipped == 'false'
uses: homarr-labs/homarr/.github/actions/extract-build-artifact@dev
with:
digest: "${{ env.GHCR_REPO }}@${{ needs.build-amd64.outputs.digest }}"
architecture: amd64
release-tag: ${{ needs.release.outputs.version }}
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
extract-asset-arm64:
name: Extract arm64 asset from docker image
needs: [release, build-arm64]
runs-on: ubuntu-24.04-arm
steps:
- name: Extract arm64
if: needs.release.outputs.skipped == 'false'
uses: homarr-labs/homarr/.github/actions/extract-build-artifact@dev
with:
digest: "${{ env.GHCR_REPO }}@${{ needs.build-arm64.outputs.digest }}"
architecture: arm64
release-tag: ${{ needs.release.outputs.version }}
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
publish:
name: Complete deployment and notify
needs: [release, build-amd64, build-arm64]
needs: [release, build-amd64, build-arm64, extract-asset-amd64, extract-asset-arm64]
runs-on: ubuntu-latest
env:
NEXT_VERSION: ${{ needs.release.outputs.version }}
@@ -222,6 +249,13 @@ jobs:
${{ env.GHCR_REPO }}@${{ needs.build-amd64.outputs.digest }} \
${{ env.GHCR_REPO }}@${{ needs.build-arm64.outputs.digest }}
- name: Publish release
if: needs.release.outputs.skipped == 'false'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release edit --repo ${{ github.repository }} ${{ needs.release.outputs.version }} --draft=false
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
@@ -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@v6
with:
fetch-depth: 0
- name: Get Next Version
@@ -12,7 +12,7 @@ jobs:
renovate-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- run: |
npx --yes --package renovate -- \
renovate-config-validator --strict .github/renovate.json5
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
permission-contents: write # required to commit to branch
permission-pull-requests: write # required to create pr & enable automerge
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
token: ${{ steps.obtainToken.outputs.token }}
- name: Setup
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
permission-pull-requests: write # required to create pr & enable automerge
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
@@ -27,7 +27,7 @@ jobs:
permission-contents: write # required to commit to branch
permission-pull-requests: write # required to create pr & enable automerge
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
- name: Setup
+1
View File
@@ -44,6 +44,7 @@
{
"successComment": false,
"failComment": false,
"draftRelease": true,
"releaseBodyTemplate": "<%= _.truncate(nextRelease.notes, { 'length': 124000, 'omission': '' }) %>"
}
]
+45
View File
@@ -1,3 +1,48 @@
## [1.46.0](https://github.com/homarr-labs/homarr/compare/v1.45.3...v1.46.0) (2025-12-12)
### Features
* **docker:** add widget options to sort containers ([#4636](https://github.com/homarr-labs/homarr/issues/4636)) ([dd24925](https://github.com/homarr-labs/homarr/commit/dd24925d906dc13d73c52a47fdd07bec89798789))
### Bug Fixes
* add autocomplete and id attributes for password manager compatibility ([#4602](https://github.com/homarr-labs/homarr/issues/4602)) ([681aee0](https://github.com/homarr-labs/homarr/commit/681aee05f07a6e09371ed089d2b9ef361494c416))
* **deps:** update dependency @tanstack/react-query to ^5.90.12 ([#4615](https://github.com/homarr-labs/homarr/issues/4615)) ([efc0384](https://github.com/homarr-labs/homarr/commit/efc0384b372fadafb65f61f715f08edd2a8f8573))
* **deps:** update dependency better-sqlite3 to ^12.5.0 ([#4569](https://github.com/homarr-labs/homarr/issues/4569)) ([a20248d](https://github.com/homarr-labs/homarr/commit/a20248d0b689ab1ca475f606edfca458897ce442))
* **deps:** update dependency drizzle-kit to ^0.31.8 ([#4618](https://github.com/homarr-labs/homarr/issues/4618)) ([5cbab28](https://github.com/homarr-labs/homarr/commit/5cbab28099d41b8ade6cc8a42e69c4191310296c))
* **deps:** update dependency drizzle-orm to ^0.45.0 ([#4619](https://github.com/homarr-labs/homarr/issues/4619)) ([cab5d19](https://github.com/homarr-labs/homarr/commit/cab5d19141a38fbb977f4ef6e9c2ddbfdfea6aff))
* **deps:** update dependency ldapts to v8.0.12 ([#4613](https://github.com/homarr-labs/homarr/issues/4613)) ([4689ec9](https://github.com/homarr-labs/homarr/commit/4689ec9933e5286c0d19c66989023b5b3376ffb1))
* **deps:** update dependency ldapts to v8.0.13 ([#4626](https://github.com/homarr-labs/homarr/issues/4626)) ([8a14a6c](https://github.com/homarr-labs/homarr/commit/8a14a6cb456a6f16975372f64451e3b54ebfd88d))
* **deps:** update dependency ldapts to v8.0.14 ([#4627](https://github.com/homarr-labs/homarr/issues/4627)) ([e37325f](https://github.com/homarr-labs/homarr/commit/e37325f5e0314b13d031fba1bff591b881baf8d1))
* **deps:** update dependency winston to v3.19.0 ([#4632](https://github.com/homarr-labs/homarr/issues/4632)) ([3b1c564](https://github.com/homarr-labs/homarr/commit/3b1c564a5abbf1d1fca6ecab46e173c8f6938130))
* **deps:** update next monorepo to v16.0.10 [security] ([#4634](https://github.com/homarr-labs/homarr/issues/4634)) ([2b2c0e7](https://github.com/homarr-labs/homarr/commit/2b2c0e7e3a813525bd977276df6e5594bc283055))
* **deps:** update react monorepo to v19.2.1 ([#4608](https://github.com/homarr-labs/homarr/issues/4608)) ([f6d16fe](https://github.com/homarr-labs/homarr/commit/f6d16fe44bf239fb85a4ee3e9ae6491131315dce))
* **deps:** update tiptap monorepo to v3 (major) ([#3612](https://github.com/homarr-labs/homarr/issues/3612)) ([d111eee](https://github.com/homarr-labs/homarr/commit/d111eee0e0a41e55de87049f3cb0a9154ae82ea1))
* **deps:** update turbo monorepo to ^2.6.2 ([#4612](https://github.com/homarr-labs/homarr/issues/4612)) ([7de45e6](https://github.com/homarr-labs/homarr/commit/7de45e6fe50ccf4333959f924889463a22316984))
* **deps:** update turbo monorepo to ^2.6.3 ([#4622](https://github.com/homarr-labs/homarr/issues/4622)) ([85689ca](https://github.com/homarr-labs/homarr/commit/85689caf6c25b80cb4ce04a0baf2980a1a4deece))
* format issues ([1dd9694](https://github.com/homarr-labs/homarr/commit/1dd9694719ec4dcefe3c8d221015ba9aecede78c))
* improve run script ([#4609](https://github.com/homarr-labs/homarr/issues/4609)) ([d084dfb](https://github.com/homarr-labs/homarr/commit/d084dfb4705fca68bb9f9465ec9564050d0b04b2))
* **plex:** test connection not fully checking connection ([#4617](https://github.com/homarr-labs/homarr/issues/4617)) ([6a90839](https://github.com/homarr-labs/homarr/commit/6a908397cd8a6189123b91ea9d68f683af2bed05))
* **transmission:** total download incorrect for cross seeded torrents ([#3211](https://github.com/homarr-labs/homarr/issues/3211)) ([0b4ca93](https://github.com/homarr-labs/homarr/commit/0b4ca93aa13e9838b481daed5703640d30a52ffc))
## [1.45.3](https://github.com/homarr-labs/homarr/compare/v1.45.2...v1.45.3) (2025-12-08)
### Bug Fixes
* **deps:** update dependency glob to v13 ([#4584](https://github.com/homarr-labs/homarr/issues/4584)) ([020cbd7](https://github.com/homarr-labs/homarr/commit/020cbd72d387c9f50f4eddcbc2685829cf7787de))
* **deps:** update dependency jotai to ^2.15.2 ([#4597](https://github.com/homarr-labs/homarr/issues/4597)) ([ed638d4](https://github.com/homarr-labs/homarr/commit/ed638d47679594eab3cc19a12473722644ebf694))
* **deps:** update dependency next-intl to v4.5.7 ([#4598](https://github.com/homarr-labs/homarr/issues/4598)) ([6b28aa8](https://github.com/homarr-labs/homarr/commit/6b28aa86d0bad29e4806592079a7f5d2e22b5ade))
* **deps:** update dependency next-intl to v4.5.8 ([#4606](https://github.com/homarr-labs/homarr/issues/4606)) ([4f92e52](https://github.com/homarr-labs/homarr/commit/4f92e5265a40c636f303406e728e88cc056da23c))
* **deps:** update dependency prettier to ^3.7.4 ([#4582](https://github.com/homarr-labs/homarr/issues/4582)) ([5480982](https://github.com/homarr-labs/homarr/commit/54809823e9edeacc245b560747b622aba683b3bf))
* **deps:** update dependency typescript-eslint to ^8.48.1 ([#4438](https://github.com/homarr-labs/homarr/issues/4438)) ([d88c602](https://github.com/homarr-labs/homarr/commit/d88c6027fe3accf7a6872ada8aa2724d30bf691c))
## [1.45.2](https://github.com/homarr-labs/homarr/compare/v1.45.1...v1.45.2) (2025-12-05)
### Bug Fixes
* add ipv6 support for internal nginx proxy ([2c48b18](https://github.com/homarr-labs/homarr/commit/2c48b18968a74cb7150c246ebb2505d07764f109))
* disable react-compiler due to issue with mantine-react-table data updates ([#4593](https://github.com/homarr-labs/homarr/issues/4593)) ([10572ca](https://github.com/homarr-labs/homarr/commit/10572ca962081961fb63b3ad9f164c610edf35e4))
## [1.45.1](https://github.com/homarr-labs/homarr/compare/v1.45.0...v1.45.1) (2025-12-03)
### Bug Fixes
+2 -1
View File
@@ -20,7 +20,8 @@ const withNextIntl = createNextIntlPlugin({
const nextConfig: NextConfig = {
output: "standalone",
reactStrictMode: true,
reactCompiler: true,
// react compiler breaks mantine-react-table, so disabled for now
//reactCompiler: true,
/** We already do typechecking as separate tasks in CI */
typescript: { ignoreBuildErrors: true },
/**
+7 -7
View File
@@ -58,7 +58,7 @@
"@mantine/tiptap": "^8.3.9",
"@million/lint": "1.0.14",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
"@tanstack/react-query-next-experimental": "^5.91.0",
"@trpc/client": "^11.7.2",
@@ -74,15 +74,15 @@
"dayjs": "^1.11.19",
"dotenv": "^17.2.3",
"flag-icons": "^7.5.0",
"glob": "^12.0.0",
"glob": "^13.0.0",
"isomorphic-dompurify": "^2.33.0",
"jotai": "^2.15.1",
"jotai": "^2.15.2",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"next": "16.0.10",
"postcss-preset-mantine": "^1.18.0",
"prismjs": "^1.30.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"react-error-boundary": "^6.0.0",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.94.2",
@@ -104,7 +104,7 @@
"concurrently": "^9.2.1",
"eslint": "^9.39.1",
"node-loader": "^2.1.0",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"typescript": "^5.9.3"
}
}
@@ -64,16 +64,23 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
<Stack gap="xl">
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} autoComplete="off" {...form.getInputProps("username")} />
<TextInput
label={t("field.username.label")}
id="username"
autoComplete="username"
{...form.getInputProps("username")}
/>
<CustomPasswordInput
withPasswordRequirements
label={t("field.password.label")}
id="password"
autoComplete="new-password"
{...form.getInputProps("password")}
/>
<PasswordInput
label={t("field.passwordConfirm.label")}
id="password-confirm"
autoComplete="new-password"
{...form.getInputProps("confirmPassword")}
/>
@@ -116,8 +116,18 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
<>
<form onSubmit={form.onSubmit((credentials) => void signInAsync(credentials.provider, credentials))}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} {...form.getInputProps("name")} />
<PasswordInput label={t("field.password.label")} {...form.getInputProps("password")} />
<TextInput
label={t("field.username.label")}
id="username"
autoComplete="username"
{...form.getInputProps("name")}
/>
<PasswordInput
label={t("field.password.label")}
id="password"
autoComplete="current-password"
{...form.getInputProps("password")}
/>
{providers.includes("credentials") && (
<Stack gap="sm">
@@ -12,7 +12,11 @@ import { groupCreateSchema } from "@homarr/validation/group";
export const InitGroup = () => {
const t = useI18n();
const { mutateAsync } = clientApi.group.createInitialExternalGroup.useMutation();
const { mutateAsync } = clientApi.group.createInitialExternalGroup.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/init");
},
});
const form = useZodForm(groupCreateSchema, {
initialValues: {
name: "",
@@ -21,9 +25,6 @@ export const InitGroup = () => {
const handleSubmitAsync = async (values: z.infer<typeof groupCreateSchema>) => {
await mutateAsync(values, {
async onSuccess() {
await revalidatePathActionAsync("/init");
},
onError(error) {
if (error.data?.code === "CONFLICT") {
form.setErrors({ name: t("common.zod.errors.custom.groupNameTaken") });
@@ -17,7 +17,11 @@ import { settingsInitSchema } from "@homarr/validation/settings";
export const InitSettings = () => {
const tSection = useScopedI18n("management.page.settings.section");
const t = useI18n();
const { mutateAsync } = clientApi.serverSettings.initSettings.useMutation();
const { mutateAsync } = clientApi.serverSettings.initSettings.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/init");
},
});
const form = useZodForm(settingsInitSchema, { initialValues: defaultServerSettings });
form.watch("analytics.enableGeneral", ({ value }) => {
@@ -31,11 +35,7 @@ export const InitSettings = () => {
});
const handleSubmitAsync = async (values: z.infer<typeof settingsInitSchema>) => {
await mutateAsync(values, {
async onSuccess() {
await revalidatePathActionAsync("/init");
},
});
await mutateAsync(values);
};
return (
@@ -26,19 +26,19 @@ export const InitUserForm = () => {
const handleSubmitAsync = async (values: FormType) => {
await mutateAsync(values, {
async onSuccess() {
onSuccess() {
showSuccessNotification({
title: tUser("notification.success.title"),
message: tUser("notification.success.message"),
});
await signIn("credentials", {
void signIn("credentials", {
name: values.username,
password: values.password,
redirect: false,
}).then(async () => {
await revalidatePathActionAsync("/init");
});
await revalidatePathActionAsync("/init");
},
onError: (error) => {
showErrorNotification({
@@ -84,9 +84,6 @@ export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) =>
id: board.id,
name: board.name,
},
onSuccess: async () => {
await revalidatePathActionAsync("/manage/boards");
},
});
}, [board.id, board.name, openDuplicateModal]);
@@ -86,7 +86,11 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
},
});
const { mutateAsync: createIntegrationAsync, isPending } = clientApi.integration.create.useMutation();
const { mutateAsync: createIntegrationAsync, isPending } = clientApi.integration.create.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/integrations");
},
});
const [error, setError] = useState<null | AnyMappedTestConnectionError>(null);
const handleSubmitAsync = async ({ appId, appHref, hasApp, ...values }: FormType) => {
@@ -116,7 +120,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
app,
},
{
async onSuccess(data) {
onSuccess(data) {
// We do it this way as we are unable to send a typesafe error through onError
if (data?.error) {
setError(data.error);
@@ -132,7 +136,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
message: t("integration.page.create.notification.success.message"),
});
await revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations"));
router.push("/manage/integrations");
},
onError: () => {
showErrorNotification({
@@ -15,7 +15,11 @@ interface RemoveCertificateProps {
export const RemoveCertificate = ({ fileName }: RemoveCertificateProps) => {
const { openConfirmModal } = useConfirmModal();
const { mutateAsync } = clientApi.certificates.removeCertificate.useMutation();
const { mutateAsync } = clientApi.certificates.removeCertificate.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/tools/certificates");
},
});
const t = useI18n();
const handleClick = () => {
@@ -27,12 +31,11 @@ export const RemoveCertificate = ({ fileName }: RemoveCertificateProps) => {
await mutateAsync(
{ fileName },
{
async onSuccess() {
onSuccess() {
showSuccessNotification({
title: t("certificate.action.remove.notification.success.title"),
message: t("certificate.action.remove.notification.success.message"),
});
await revalidatePathActionAsync("/manage/tools/certificates");
},
onError() {
showErrorNotification({
@@ -15,7 +15,11 @@ interface RemoveHostnameActionIconProps {
}
export const RemoveHostnameActionIcon = (input: RemoveHostnameActionIconProps) => {
const { mutateAsync } = clientApi.certificates.removeTrustedHostname.useMutation();
const { mutateAsync } = clientApi.certificates.removeTrustedHostname.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/tools/certificates/hostnames");
},
});
const { openConfirmModal } = useConfirmModal();
const t = useI18n();
@@ -26,8 +30,7 @@ export const RemoveHostnameActionIcon = (input: RemoveHostnameActionIconProps) =
// eslint-disable-next-line no-restricted-syntax
async onConfirm() {
await mutateAsync(input, {
async onSuccess() {
await revalidatePathActionAsync("/manage/tools/certificates/hostnames");
onSuccess() {
showSuccessNotification({
title: t("certificate.action.removeHostname.notification.success.title"),
message: t("certificate.action.removeHostname.notification.success.message"),
@@ -206,15 +206,16 @@ const ContainerActionBarButton = (props: ContainerActionBarButtonProps) => {
const t = useScopedI18n("docker.action");
const utils = clientApi.useUtils();
const { mutateAsync, isPending } = clientApi.docker[`${props.action}All`].useMutation();
const { mutateAsync, isPending } = clientApi.docker[`${props.action}All`].useMutation({
async onSettled() {
await utils.docker.getContainers.invalidate();
},
});
const handleClickAsync = async () => {
await mutateAsync(
{ ids: props.selectedIds },
{
async onSettled() {
await utils.docker.getContainers.invalidate();
},
onSuccess() {
showSuccessNotification({
title: t(`${props.action}.notification.success.title`),
@@ -18,7 +18,12 @@ interface UserProfileAvatarForm {
}
export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
const { mutate } = clientApi.user.setProfileImage.useMutation();
const { mutate } = clientApi.user.setProfileImage.useMutation({
async onSuccess() {
// Revalidate all as the avatar is used in multiple places
await revalidatePathActionAsync("/");
},
});
const [opened, { toggle }] = useDisclosure(false);
const { openConfirmModal } = useConfirmModal();
const t = useI18n();
@@ -38,9 +43,7 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
image: base64Url,
},
{
async onSuccess() {
// Revalidate all as the avatar is used in multiple places
await revalidatePathActionAsync("/");
onSuccess() {
showSuccessNotification({
message: tManageAvatar("changeImage.notification.success.message"),
});
@@ -74,9 +77,7 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
image: null,
},
{
async onSuccess() {
// Revalidate all as the avatar is used in multiple places
await revalidatePathActionAsync("/");
onSuccess() {
showSuccessNotification({
message: tManageAvatar("removeImage.notification.success.message"),
});
@@ -25,7 +25,11 @@ export const TransferGroupOwnership = ({ group }: TransferGroupOwnershipProps) =
const [innerOwnerId, setInnerOwnerId] = useState(group.ownerId);
const { openModal } = useModalAction(UserSelectModal);
const { openConfirmModal } = useConfirmModal();
const { mutateAsync } = clientApi.group.transferOwnership.useMutation();
const { mutateAsync } = clientApi.group.transferOwnership.useMutation({
async onSuccess() {
await revalidatePathActionAsync(`/manage/users/groups/${group.id}`);
},
});
const handleTransfer = useCallback(() => {
openModal(
@@ -47,7 +51,7 @@ export const TransferGroupOwnership = ({ group }: TransferGroupOwnershipProps) =
userId: id,
},
{
async onSuccess() {
onSuccess() {
setInnerOwnerId(id);
showSuccessNotification({
title: tRoot("common.notification.transfer.success"),
@@ -56,7 +60,6 @@ export const TransferGroupOwnership = ({ group }: TransferGroupOwnershipProps) =
user: name,
}),
});
await revalidatePathActionAsync(`/manage/users/groups/${group.id}`);
},
onError() {
showErrorNotification({
@@ -56,16 +56,19 @@ export const GroupsTable = ({ groups, initialGroupIds, hasFilter }: GroupsTableP
() => initialGroupIds.some((groupId, index) => groupIds.indexOf(groupId) !== index),
[groupIds, initialGroupIds],
);
const { mutateAsync, isPending } = clientApi.group.savePositions.useMutation();
const { mutateAsync, isPending } = clientApi.group.savePositions.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/users/groups");
},
});
const handleSavePositionsAsync = async () => {
await mutateAsync(
{ positions: groupIds },
{
async onSuccess() {
onSuccess() {
showSuccessNotification({
message: t("group.action.changePosition.notification.success.message"),
});
await revalidatePathActionAsync("/manage/users/groups");
},
onError() {
showSuccessNotification({
+2 -2
View File
@@ -49,9 +49,9 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^24.10.1",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"tsx": "4.20.4",
"typescript": "^5.9.3"
}
+2 -2
View File
@@ -34,9 +34,9 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/ws": "^8.18.1",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"typescript": "^5.9.3"
}
}
+2 -1
View File
@@ -5,6 +5,7 @@ events {
http {
server {
listen 7575;
listen [::]:7575;
# Route websockets traffic to port 3001
location /websockets {
@@ -25,4 +26,4 @@ http {
client_max_body_size 32M;
}
}
}
}
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.45.1",
"version": "1.46.0",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -43,21 +43,21 @@
"@semantic-release/npm": "^13.1.2",
"@semantic-release/release-notes-generator": "^14.1.0",
"@testcontainers/redis": "^11.9.0",
"@turbo/gen": "^2.6.1",
"@turbo/gen": "^2.6.3",
"@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.0.14",
"@vitest/ui": "^4.0.14",
"@vitest/coverage-v8": "^4.0.15",
"@vitest/ui": "^4.0.15",
"conventional-changelog-conventionalcommits": "^9.1.0",
"cross-env": "^10.1.0",
"jsdom": "^27.2.0",
"json5": "^2.2.3",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"semantic-release": "^25.0.2",
"testcontainers": "^11.9.0",
"turbo": "^2.6.1",
"turbo": "^2.6.3",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^4.0.14"
"vitest": "^4.0.15"
},
"packageManager": "pnpm@10.24.0",
"engines": {
@@ -82,20 +82,20 @@
"axios@>=1.0.0 <1.8.2": ">=1.13.2",
"brace-expansion@>=2.0.0 <=2.0.1": ">=4.0.1",
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
"esbuild@<=0.24.2": ">=0.27.0",
"esbuild@<=0.24.2": ">=0.27.1",
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
"hono@<4.6.5": ">=4.10.7",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.16.0",
"react-is": "^19.2.0",
"react-is": "^19.2.1",
"rollup@>=4.0.0 <4.22.4": ">=4.53.3",
"sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
"tmp@<=0.2.3": ">=0.2.5",
"vite@>=5.0.0 <=5.4.18": ">=7.2.4"
"vite@>=5.0.0 <=5.4.18": ">=7.2.6"
},
"patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch",
+5 -5
View File
@@ -43,15 +43,15 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@kubernetes/client-node": "^1.4.0",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@trpc/client": "^11.7.2",
"@trpc/react-query": "^11.7.2",
"@trpc/server": "^11.7.2",
"@trpc/tanstack-react-query": "^11.7.2",
"lodash.clonedeep": "^4.5.0",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"superjson": "2.2.6",
"trpc-to-openapi": "^3.1.0",
"zod": "^4.1.13"
@@ -61,7 +61,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.1",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"typescript": "^5.9.3"
}
}
+5 -5
View File
@@ -34,11 +34,11 @@
"@homarr/validation": "workspace:^0.1.0",
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.0.9",
"next": "16.0.7",
"ldapts": "8.0.14",
"next": "16.0.10",
"next-auth": "5.0.0-beta.30",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {
@@ -48,7 +48,7 @@
"@types/bcrypt": "6.0.0",
"@types/cookies": "0.9.2",
"eslint": "^9.39.1",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"typescript": "^5.9.3"
}
}
@@ -6,14 +6,14 @@ import type { Database, InferInsertModel } from "@homarr/db";
import { and, eq } from "@homarr/db";
import { users } from "@homarr/db/schema";
import { logger } from "@homarr/log";
import type { userSignInSchema } from "@homarr/validation/user";
import type { ldapSignInSchema } from "@homarr/validation/user";
import { env } from "../../../env";
import { LdapClient } from "../ldap-client";
export const authorizeWithLdapCredentialsAsync = async (
db: Database,
credentials: z.infer<typeof userSignInSchema>,
credentials: z.infer<typeof ldapSignInSchema>,
) => {
logger.info(`user ${credentials.name} is trying to log in using LDAP. Connecting to LDAP server...`);
const client = new LdapClient();
@@ -38,7 +38,14 @@ export const authorizeWithLdapCredentialsAsync = async (
attributes: [env.AUTH_LDAP_USERNAME_ATTRIBUTE, env.AUTH_LDAP_USER_MAIL_ATTRIBUTE],
},
})
.then((entries) => entries.at(0));
.then((entries) => {
if (entries.length > 1) {
logger.warn(`Multiple LDAP users found for ${credentials.name}, expected only one.`);
throw new CredentialsSignin();
}
return entries.at(0);
});
if (!ldapUser) {
logger.warn(`User ${credentials.name} not found in LDAP`);
@@ -1,7 +1,7 @@
import type Credentials from "@auth/core/providers/credentials";
import type { Database } from "@homarr/db";
import { userSignInSchema } from "@homarr/validation/user";
import { ldapSignInSchema, userSignInSchema } from "@homarr/validation/user";
import { authorizeWithBasicCredentialsAsync } from "./authorization/basic-authorization";
import { authorizeWithLdapCredentialsAsync } from "./authorization/ldap-authorization";
@@ -28,7 +28,7 @@ export const createLdapConfiguration = (db: Database) =>
name: "Ldap",
// eslint-disable-next-line no-restricted-syntax
async authorize(credentials) {
const data = await userSignInSchema.parseAsync(credentials);
const data = await ldapSignInSchema.parseAsync(credentials);
return await authorizeWithLdapCredentialsAsync(db, data).catch(() => null);
},
}) satisfies CredentialsConfiguration;
+2 -2
View File
@@ -25,8 +25,8 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/api": "workspace:^0.1.0",
"react": "19.2.0",
"react-dom": "19.2.0"
"react": "19.2.1",
"react-dom": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -34,7 +34,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"typescript": "^5.9.3"
}
+3 -3
View File
@@ -32,10 +32,10 @@
"@paralleldrive/cuid2": "^3.1.0",
"dayjs": "^1.11.19",
"dns-caching": "^0.2.7",
"next": "16.0.7",
"next": "16.0.10",
"octokit": "^5.0.5",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"undici": "7.16.0",
"zod": "^4.1.13",
"zod-validation-error": "^5.0.0"
+2 -2
View File
@@ -29,12 +29,12 @@
"@homarr/core": "workspace:^0.1.0",
"@homarr/cron-jobs": "workspace:^0.1.0",
"@homarr/log": "workspace:^0.1.0",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@trpc/client": "^11.7.2",
"@trpc/server": "^11.7.2",
"@trpc/tanstack-react-query": "^11.7.2",
"node-cron": "^4.2.1",
"react": "19.2.0",
"react": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {
+5 -5
View File
@@ -53,10 +53,10 @@
"@paralleldrive/cuid2": "^3.1.0",
"@testcontainers/mysql": "^11.9.0",
"@testcontainers/postgresql": "^11.9.0",
"better-sqlite3": "^12.4.1",
"better-sqlite3": "^12.5.0",
"dotenv": "^17.2.3",
"drizzle-kit": "^0.31.7",
"drizzle-orm": "^0.44.7",
"drizzle-kit": "^0.31.8",
"drizzle-orm": "^0.45.0",
"drizzle-zod": "^0.8.3",
"mysql2": "3.15.3",
"pg": "^8.16.3",
@@ -69,9 +69,9 @@
"@types/better-sqlite3": "7.6.13",
"@types/pg": "^8.15.6",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"tsx": "4.20.4",
"typescript": "^5.9.3"
}
+4 -2
View File
@@ -1,7 +1,9 @@
import type { ChangeEvent, FocusEvent } from "react";
export interface InputPropsFor<T, TOnChangeArg, TComponent extends HTMLElement = HTMLInputElement>
extends BasePropsFor<TOnChangeArg, TComponent> {
export interface InputPropsFor<T, TOnChangeArg, TComponent extends HTMLElement = HTMLInputElement> extends BasePropsFor<
TOnChangeArg,
TComponent
> {
value?: T;
defaultValue?: T;
}
+1 -1
View File
@@ -31,7 +31,7 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.9",
"react": "19.2.0",
"react": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {
@@ -16,7 +16,19 @@ interface UploadMediaProps {
export const UploadMedia = ({ children, onSettled, onSuccess, multiple = false }: UploadMediaProps) => {
const t = useI18n();
const { mutateAsync, isPending } = clientApi.media.uploadMedia.useMutation();
const { mutateAsync, isPending } = clientApi.media.uploadMedia.useMutation({
async onSuccess(mediaIds) {
await onSuccess?.(
mediaIds.map((id) => ({
id,
url: `/api/user-medias/${id}`,
})),
);
},
async onSettled() {
await onSettled?.();
},
});
const handleFileUploadAsync = async (files: File[] | File | null) => {
if (!files || (Array.isArray(files) && files.length === 0)) return;
@@ -24,25 +36,16 @@ export const UploadMedia = ({ children, onSettled, onSuccess, multiple = false }
const formData = new FormData();
filesArray.forEach((file) => formData.append("files", file));
await mutateAsync(formData, {
async onSuccess(mediaIds) {
onSuccess() {
showSuccessNotification({
message: t("media.action.upload.notification.success.message"),
});
await onSuccess?.(
mediaIds.map((id) => ({
id,
url: `/api/user-medias/${id}`,
})),
);
},
onError() {
showErrorNotification({
message: t("media.action.upload.notification.error.message"),
});
},
async onSettled() {
await onSettled?.();
},
});
};
@@ -46,6 +46,7 @@ export class TransmissionIntegration extends Integration implements IDownloadCli
name: torrent.name,
size: torrent.totalSize,
sent: torrent.uploadedEver,
received: torrent.downloadedEver,
downSpeed: torrent.percentDone !== 1 ? torrent.rateDownload : undefined,
upSpeed: torrent.rateUpload,
time:
@@ -26,6 +26,8 @@ export const downloadClientItemSchema = z.object({
size: z.number(),
/** Total uploaded in Bytes, only required for Torrent items */
sent: z.number().optional(),
/** Total downloaded in Bytes, only required for Torrent items */
received: z.number().optional(),
/** Download speed in Bytes/s, only required if not complete
* (Says 0 only if it should be downloading but isn't) */
downSpeed: z.number().optional(),
@@ -169,17 +169,15 @@ export class PlexIntegration extends Integration implements IMediaServerIntegrat
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
const token = super.getSecretValue("apiKey");
const response = await input.fetchAsync(this.url("/"), {
const response = await input.fetchAsync(super.url("/prefs"), {
headers: {
"X-Plex-Token": token,
Accept: "application/json",
},
});
if (!response.ok) return TestConnectionError.StatusResult(response);
const result = await response.text();
await PlexIntegration.parseXmlAsync<PlexResponse>(result);
return { success: true };
}
+1 -1
View File
@@ -26,7 +26,7 @@
"dependencies": {
"@homarr/core": "workspace:^0.1.0",
"superjson": "2.2.6",
"winston": "3.18.3",
"winston": "3.19.0",
"zod": "^4.1.13"
},
"devDependencies": {
+3 -3
View File
@@ -36,9 +36,9 @@
"@mantine/core": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"dayjs": "^1.11.19",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {
@@ -2,7 +2,6 @@ import type { z } from "zod/v4";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { MaybePromise } from "@homarr/common/types";
import { AppForm } from "@homarr/forms-collection";
import { createModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
@@ -10,7 +9,7 @@ import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { appManageSchema } from "@homarr/validation/app";
interface QuickAddAppModalProps {
onClose: (createdApp: Omit<RouterOutputs["app"]["create"], "appId">) => MaybePromise<void>;
onClose: (createdApp: Omit<RouterOutputs["app"]["create"], "appId">) => void;
}
export const QuickAddAppModal = createModal<QuickAddAppModalProps>(({ actions, innerProps }) => {
@@ -28,13 +27,13 @@ export const QuickAddAppModal = createModal<QuickAddAppModalProps>(({ actions, i
const handleSubmit = (values: z.infer<typeof appManageSchema>) => {
mutate(values, {
async onSuccess(app) {
onSuccess(app) {
showSuccessNotification({
title: tScoped("success.title"),
message: tScoped("success.message"),
});
await innerProps.onClose(app);
innerProps.onClose(app);
actions.closeModal();
},
});
@@ -1,7 +1,7 @@
import { Button, Group, Stack, Text, TextInput } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import type { MaybePromise } from "@homarr/common/types";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
@@ -15,7 +15,6 @@ interface InnerProps {
id: string;
name: string;
};
onSuccess: () => MaybePromise<void>;
}
export const DuplicateBoardModal = createModal<InnerProps>(({ actions, innerProps }) => {
@@ -27,7 +26,11 @@ export const DuplicateBoardModal = createModal<InnerProps>(({ actions, innerProp
},
});
const boardNameStatus = useBoardNameStatus(form.values.name);
const { mutateAsync, isPending } = clientApi.board.duplicateBoard.useMutation();
const { mutateAsync, isPending } = clientApi.board.duplicateBoard.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/boards");
},
});
return (
<form
@@ -40,13 +43,12 @@ export const DuplicateBoardModal = createModal<InnerProps>(({ actions, innerProp
id: innerProps.board.id,
},
{
async onSuccess() {
onSuccess() {
actions.closeModal();
showSuccessNotification({
title: t("board.action.duplicate.notification.success.title"),
message: t("board.action.duplicate.notification.success.message"),
});
await innerProps.onSuccess();
},
onError() {
showErrorNotification({
@@ -65,7 +65,11 @@ export const ImportBoardModal = createModal(({ actions }) => {
},
);
const { mutateAsync, isPending } = clientApi.board.importOldmarrConfig.useMutation();
const { mutateAsync, isPending } = clientApi.board.importOldmarrConfig.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/boards");
},
});
const boardNameStatus = useBoardNameStatus(form.values.configuration.name);
const handleSubmitAsync = async (values: { file: File; configuration: OldmarrImportConfiguration }) => {
@@ -74,9 +78,8 @@ export const ImportBoardModal = createModal(({ actions }) => {
formData.set("configuration", JSON.stringify(values.configuration));
await mutateAsync(formData, {
async onSuccess() {
onSuccess() {
actions.closeModal();
await revalidatePathActionAsync("/manage/boards");
showSuccessNotification({
title: tOldImport("notification.success.title"),
message: tOldImport("notification.success.message"),
@@ -27,7 +27,11 @@ export const AddCertificateModal = createModal<InnerProps>(({ actions, innerProp
},
},
);
const { mutateAsync } = clientApi.certificates.addCertificate.useMutation();
const { mutateAsync } = clientApi.certificates.addCertificate.useMutation({
async onSuccess() {
await innerProps.onSuccess?.();
},
});
return (
<form
@@ -35,12 +39,11 @@ export const AddCertificateModal = createModal<InnerProps>(({ actions, innerProp
const formData = new FormData();
formData.set("file", values.file);
await mutateAsync(formData, {
async onSuccess() {
onSuccess() {
showSuccessNotification({
title: t("certificate.action.create.notification.success.title"),
message: t("certificate.action.create.notification.success.message"),
});
await innerProps.onSuccess?.();
actions.closeModal();
},
onError() {
+1 -1
View File
@@ -26,7 +26,7 @@
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"react": "19.2.0"
"react": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+3 -3
View File
@@ -40,9 +40,9 @@
"@mantine/core": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"adm-zip": "0.5.16",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"superjson": "2.2.6",
"zod": "^4.1.13",
"zod-form-data": "^3.0.1"
@@ -29,10 +29,10 @@ export const ImportTokenModal = createModal<InnerProps>(({ actions, innerProps }
mutate(
{ checksum: innerProps.checksum, token: values.token },
{
async onSuccess(isValid) {
onSuccess(isValid) {
if (isValid) {
actions.closeModal();
await innerProps.onSuccessAsync(values.token);
void innerProps.onSuccessAsync(values.token);
} else {
showErrorNotification({
title: tTokenModal("notification.error.title"),
+3 -3
View File
@@ -27,9 +27,9 @@
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^8.3.9",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0"
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+4 -4
View File
@@ -37,10 +37,10 @@
"@mantine/hooks": "^8.3.9",
"@mantine/spotlight": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"jotai": "^2.15.1",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"jotai": "^2.15.2",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"use-deep-compare-effect": "^1.8.1"
},
"devDependencies": {
+4 -4
View File
@@ -32,10 +32,10 @@
"dayjs": "^1.11.19",
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"next-intl": "4.5.6",
"react": "19.2.0",
"react-dom": "19.2.0"
"next": "16.0.10",
"next-intl": "4.5.8",
"react": "19.2.1",
"react-dom": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -2232,7 +2232,7 @@
"unknown": "Ukendt",
"pending": "Afventende",
"processing": "Behandler",
"requested": "",
"requested": "Anmodet",
"partiallyAvailable": "Delvis",
"available": "Tilgængelig",
"blacklisted": "Blacklistet",
+17 -1
View File
@@ -1939,7 +1939,23 @@
"dockerContainers": {
"name": "Docker stats",
"description": "Stats of your containers (This widget can only be added with administrator privileges)",
"option": {},
"option": {
"enableRowSorting": {
"label": "Enable items sorting"
},
"defaultSort": {
"label": "Column used for sorting by default",
"option": {
"name": "Name",
"state": "State",
"cpuUsage": "CPU usage",
"memoryUsage": "Memory usage"
}
},
"descendingDefaultSort": {
"label": "Invert sorting"
}
},
"error": {
"internalServerError": "Failed to fetch containers stats"
}
+2 -2
View File
@@ -747,7 +747,7 @@
},
"statusCode": {
"title": "Antwoord fout",
"description": "",
"description": "Onverwachte {statusCode} ({reason}) reactie van <url></url>. Controleer of de URL wijst naar de basis-URL van de integratie.",
"otherDescription": "",
"reason": {
"badRequest": "Onjuist verzoek",
@@ -2232,7 +2232,7 @@
"unknown": "Onbekend",
"pending": "In afwachting",
"processing": "Verwerken",
"requested": "",
"requested": "Aangevraagd",
"partiallyAvailable": "Gedeeltelijk",
"available": "Beschikbaar",
"blacklisted": "Geblacklist",
+1 -1
View File
@@ -341,7 +341,7 @@
"description": ""
},
"full-all": {
"label": "",
"label": "Acesso completo a aplicativos",
"description": ""
}
}
+3 -3
View File
@@ -35,9 +35,9 @@
"@mantine/hooks": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"svgson": "^5.3.1"
},
"devDependencies": {
@@ -10,8 +10,10 @@ interface BaseSelectItem {
label: string;
}
export interface SelectWithCustomItemsProps<TSelectItem extends BaseSelectItem>
extends Pick<SelectProps, "label" | "error" | "defaultValue" | "value" | "onChange" | "placeholder" | "clearable"> {
export interface SelectWithCustomItemsProps<TSelectItem extends BaseSelectItem> extends Pick<
SelectProps,
"label" | "error" | "defaultValue" | "value" | "onChange" | "placeholder" | "clearable"
> {
data: TSelectItem[];
description?: string;
withAsterisk?: boolean;
+11
View File
@@ -69,6 +69,17 @@ export const userSignInSchema = z.object({
password: z.string().min(1),
});
export const ldapSignInSchema = z.object({
name: z
.string()
.min(1)
// Prevent special characters that could lead to LDAP injection attacks
.regex(/^[^\\,+<>;"=)(*|!&]+$/, {
message: "Invalid characters in ldap username",
}),
password: z.string().min(1),
});
export const userRegistrationSchema = addConfirmPasswordRefinement(
z.object({
username: usernameSchema,
+19 -19
View File
@@ -52,29 +52,29 @@
"@mantine/core": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"@tiptap/extension-color": "2.27.1",
"@tiptap/extension-highlight": "2.27.1",
"@tiptap/extension-image": "2.27.1",
"@tiptap/extension-link": "^2.27.1",
"@tiptap/extension-placeholder": "^2.27.1",
"@tiptap/extension-table": "2.27.1",
"@tiptap/extension-table-cell": "2.27.1",
"@tiptap/extension-table-header": "2.27.1",
"@tiptap/extension-table-row": "2.27.1",
"@tiptap/extension-task-item": "2.27.1",
"@tiptap/extension-task-list": "2.27.1",
"@tiptap/extension-text-align": "2.27.1",
"@tiptap/extension-text-style": "2.27.1",
"@tiptap/extension-underline": "2.27.1",
"@tiptap/react": "^2.27.1",
"@tiptap/starter-kit": "^2.27.1",
"@tiptap/extension-color": "3.13.0",
"@tiptap/extension-highlight": "3.13.0",
"@tiptap/extension-image": "3.13.0",
"@tiptap/extension-link": "^3.13.0",
"@tiptap/extension-placeholder": "^3.13.0",
"@tiptap/extension-table": "3.13.0",
"@tiptap/extension-table-cell": "3.13.0",
"@tiptap/extension-table-header": "3.13.0",
"@tiptap/extension-table-row": "3.13.0",
"@tiptap/extension-task-item": "3.13.0",
"@tiptap/extension-task-list": "3.13.0",
"@tiptap/extension-text-align": "3.13.0",
"@tiptap/extension-text-style": "3.13.0",
"@tiptap/extension-underline": "3.13.0",
"@tiptap/react": "^3.13.0",
"@tiptap/starter-kit": "^3.13.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"mantine-form-zod-resolver": "^1.3.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"react-markdown": "^10.1.0",
"recharts": "^2.15.4",
"video.js": "^8.23.4",
@@ -69,10 +69,10 @@ export const WidgetAppInput = ({ property, kind }: CommonWidgetInputProps<"app">
variant="default"
onClick={() =>
openModal({
// eslint-disable-next-line no-restricted-syntax
async onClose(createdAppId) {
await refetch();
form.setFieldValue(`options.${property}`, createdAppId);
onClose(createdAppId) {
void refetch().then(() => {
form.setFieldValue(`options.${property}`, createdAppId);
});
},
})
}
+28 -8
View File
@@ -56,6 +56,7 @@ const createColumns = (
t: ReturnType<typeof useScopedI18n<"docker">>,
): MRT_ColumnDef<RouterOutputs["docker"]["getContainers"]["containers"][number]>[] => [
{
id: "name",
accessorKey: "name",
header: t("field.name.label"),
Cell({ renderedCellValue, row }) {
@@ -70,6 +71,7 @@ const createColumns = (
},
},
{
id: "state",
accessorKey: "state",
size: 100,
header: t("field.state.label"),
@@ -78,6 +80,13 @@ const createColumns = (
},
},
{
id: "cpuUsage",
sortingFn: (rowA, rowB) => {
const cpuUsageA = safeValue(rowA.original.cpuUsage);
const cpuUsageB = safeValue(rowB.original.cpuUsage);
return cpuUsageA - cpuUsageB;
},
accessorKey: "cpuUsage",
size: 80,
header: t("field.stats.cpu.label"),
@@ -92,6 +101,13 @@ const createColumns = (
},
},
{
id: "memoryUsage",
sortingFn: (rowA, rowB) => {
const memoryUsageA = safeValue(rowA.original.memoryUsage);
const memoryUsageB = safeValue(rowB.original.memoryUsage);
return memoryUsageA - memoryUsageB;
},
accessorKey: "memoryUsage",
size: 80,
header: t("field.stats.memory.label"),
@@ -106,14 +122,20 @@ const createColumns = (
},
},
{
id: "actions",
accessorKey: "actions",
size: 80,
header: t("action.title"),
enableSorting: false,
Cell({ row }) {
const utils = clientApi.useUtils();
const { mutateAsync: startContainer } = clientApi.docker.startAll.useMutation();
const { mutateAsync: stopContainer } = clientApi.docker.stopAll.useMutation();
const { mutateAsync: restartContainer } = clientApi.docker.restartAll.useMutation();
// eslint-disable-next-line no-restricted-syntax
const onSettled = async () => {
await utils.docker.getContainers.invalidate();
};
const { mutateAsync: startContainer } = clientApi.docker.startAll.useMutation({ onSettled });
const { mutateAsync: stopContainer } = clientApi.docker.stopAll.useMutation({ onSettled });
const { mutateAsync: restartContainer } = clientApi.docker.restartAll.useMutation({ onSettled });
const handleActionAsync = async (action: "start" | "stop" | "restart") => {
const mutation = action === "start" ? startContainer : action === "stop" ? stopContainer : restartContainer;
@@ -121,9 +143,6 @@ const createColumns = (
await mutation(
{ ids: [row.original.id] },
{
async onSettled() {
await utils.docker.getContainers.invalidate();
},
onSuccess() {
showSuccessNotification({
title: t(`action.${action}.notification.success.title`),
@@ -167,7 +186,7 @@ const createColumns = (
},
];
export default function DockerWidget({ width }: WidgetComponentProps<"dockerContainers">) {
export default function DockerWidget({ options, width, isEditMode }: WidgetComponentProps<"dockerContainers">) {
const t = useScopedI18n("docker");
const isTiny = width <= 256;
@@ -191,8 +210,8 @@ export default function DockerWidget({ width }: WidgetComponentProps<"dockerCont
enablePagination: false,
enableTopToolbar: false,
enableBottomToolbar: false,
enableSorting: false,
enableColumnActions: false,
enableSorting: options.enableRowSorting && !isEditMode,
enableStickyHeader: false,
enableColumnOrdering: false,
enableRowSelection: false,
@@ -202,6 +221,7 @@ export default function DockerWidget({ width }: WidgetComponentProps<"dockerCont
enableFilters: false,
enableHiding: false,
initialState: {
sorting: [{ id: options.defaultSort, desc: options.descendingDefaultSort }],
density: "xs",
},
mantinePaperProps: {
+24 -1
View File
@@ -1,12 +1,35 @@
import { IconBrandDocker, IconServerOff } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { createWidgetDefinition } from "../definition";
import { optionsBuilder } from "../options";
const columnsList = [
"name",
"state",
"cpuUsage",
"memoryUsage",
] as const satisfies (keyof RouterOutputs["docker"]["getContainers"]["containers"][number])[];
export const { definition, componentLoader } = createWidgetDefinition("dockerContainers", {
icon: IconBrandDocker,
createOptions() {
return optionsBuilder.from(() => ({}));
return optionsBuilder.from((factory) => ({
enableRowSorting: factory.switch({
defaultValue: false,
}),
defaultSort: factory.select({
defaultValue: "name",
options: columnsList.map((value) => ({
value,
label: (t) => t(`widget.dockerContainers.option.defaultSort.option.${value}`),
})),
}),
descendingDefaultSort: factory.switch({
defaultValue: false,
}),
}));
},
errors: {
INTERNAL_SERVER_ERROR: {
+9 -3
View File
@@ -36,6 +36,7 @@ import {
IconX,
} from "@tabler/icons-react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef, MRT_VisibilityState } from "mantine-react-table";
import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
@@ -48,6 +49,8 @@ import { useScopedI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "../definition";
dayjs.extend(relativeTime);
interface QuickFilter {
integrationKinds: string[];
statuses: ExtendedDownloadClientItem["state"][];
@@ -188,14 +191,14 @@ export default function DownloadClientsWidget({
)
//Add extrapolated data and actions if user is allowed interaction
.map((item): ExtendedDownloadClientItem => {
const received = Math.floor(item.size * item.progress);
const received = item.received ?? Math.floor(item.size * item.progress);
const integrationIds = [pair.integration.id];
return {
integration: pair.integration,
...item,
category: item.category !== undefined && item.category.length > 0 ? item.category : undefined,
received,
ratio: item.sent !== undefined ? item.sent / (received || 1) : undefined,
ratio: item.sent !== undefined ? item.sent / item.size : undefined,
//Only add if permission to use mutations
actions: integrationsWithInteractions.includes(pair.integration.id)
? {
@@ -714,7 +717,10 @@ const ItemInfoModal = ({ items, currentIndex, opened, onClose }: ItemInfoModalPr
/>
{item.type !== "miscellaneous" && <NormalizedLine itemKey="ratio" values={item.ratio} />}
<NormalizedLine itemKey="added" values={item.added === undefined ? "unknown" : dayjs(item.added).format()} />
<NormalizedLine itemKey="time" values={item.time !== 0 ? dayjs().add(item.time).format() : "∞"} />
<NormalizedLine
itemKey="time"
values={item.time !== 0 ? dayjs().add(item.time, "milliseconds").fromNow() : "∞"}
/>
<NormalizedLine itemKey="category" values={item.category} />
</Stack>
)}
+18 -16
View File
@@ -41,21 +41,21 @@ import {
IconX,
} from "@tabler/icons-react";
import { Color } from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import Image from "@tiptap/extension-image";
import Placeholder from "@tiptap/extension-placeholder";
import Table from "@tiptap/extension-table";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import TableRow from "@tiptap/extension-table-row";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextAlign from "@tiptap/extension-text-align";
import TextStyle from "@tiptap/extension-text-style";
import Underline from "@tiptap/extension-underline";
import { Highlight } from "@tiptap/extension-highlight";
import { Image } from "@tiptap/extension-image";
import { Placeholder } from "@tiptap/extension-placeholder";
import { Table } from "@tiptap/extension-table";
import { TableCell } from "@tiptap/extension-table-cell";
import { TableHeader } from "@tiptap/extension-table-header";
import { TableRow } from "@tiptap/extension-table-row";
import { TaskItem } from "@tiptap/extension-task-item";
import { TaskList } from "@tiptap/extension-task-list";
import { TextAlign } from "@tiptap/extension-text-align";
import { TextStyle } from "@tiptap/extension-text-style";
import type { Editor } from "@tiptap/react";
import { BubbleMenu, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useEditor } from "@tiptap/react";
import { BubbleMenu } from "@tiptap/react/menus";
import { StarterKit } from "@tiptap/starter-kit";
import type { Node } from "prosemirror-model";
import { clientApi } from "@homarr/api/client";
@@ -133,7 +133,8 @@ export function Notebook({ options, setOptions, isEditMode, boardId, itemId }: W
};
},
}),
StarterKit,
// we use a custom link implementation from mantine
StarterKit.configure({ link: false }),
Table.configure({
resizable: true,
lastColumnResizable: false,
@@ -170,8 +171,9 @@ export function Notebook({ options, setOptions, isEditMode, boardId, itemId }: W
TaskList.configure({ itemTypeName: "taskItem" }),
TextAlign.configure({ types: ["heading", "paragraph"] }),
TextStyle,
Underline,
],
shouldRerenderOnTransaction: true,
immediatelyRender: false,
content,
onUpdate: ({ editor }) => {
setContent(editor.getHTML());
+10 -6
View File
@@ -18,14 +18,17 @@ interface TextInput extends CommonInput<string> {
validate?: z.ZodType<string>;
}
interface MultiSelectInput<TOptions extends SelectOption[]>
extends CommonInput<inferSelectOptionValue<TOptions[number]>[]> {
interface MultiSelectInput<TOptions extends SelectOption[]> extends CommonInput<
inferSelectOptionValue<TOptions[number]>[]
> {
options: TOptions;
searchable?: boolean;
}
export interface SortableItemListInput<TItem, TOptionValue extends UniqueIdentifier>
extends Omit<CommonInput<TOptionValue[]>, "withDescription"> {
export interface SortableItemListInput<TItem, TOptionValue extends UniqueIdentifier> extends Omit<
CommonInput<TOptionValue[]>,
"withDescription"
> {
AddButton: (props: { addItem: (item: TItem) => void; values: TOptionValue[] }) => React.ReactNode;
ItemComponent: (props: {
item: TItem;
@@ -37,8 +40,9 @@ export interface SortableItemListInput<TItem, TOptionValue extends UniqueIdentif
useData: (values: TOptionValue[]) => { data: TItem[] | undefined; isLoading: boolean; error: unknown };
}
interface SelectInput<TOptions extends readonly SelectOption[]>
extends CommonInput<inferSelectOptionValue<TOptions[number]>> {
interface SelectInput<TOptions extends readonly SelectOption[]> extends CommonInput<
inferSelectOptionValue<TOptions[number]>
> {
options: TOptions;
searchable?: boolean;
}
+1187 -1195
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -5,4 +5,4 @@ packages:
minimumReleaseAge: 4320 # Only download deps if they are at least 3 days old (in minutes)
minimumReleaseAgeExclude:
- next
- "@next/eslint-plugin-next"
- "@next/*"
+12 -6
View File
@@ -1,10 +1,12 @@
#!/usr/bin/env bash
# Create sub directories in volume
mkdir -p /appdata/db
mkdir -p /appdata/redis
mkdir -p /appdata/trusted-certificates
# Run migrations
if [ $DB_MIGRATIONS_DISABLED = "true" ]; then
if [ "$DB_MIGRATIONS_DISABLED" = "true" ]; then
echo "DB migrations are disabled, skipping"
else
echo "Running DB migrations"
@@ -20,13 +22,15 @@ export CRON_JOB_API_KEY=$(openssl rand -base64 32)
# 1. Replace the HOSTNAME in the nginx template file
# 2. Create the nginx configuration file from the template
# 3. Start the nginx server
export HOSTNAME
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
# Start services in the background and store their PIDs
nginx -g 'daemon off;' &
NGINX_PID=$!
if [ $REDIS_IS_EXTERNAL = "true" ]; then
if [ "$REDIS_IS_EXTERNAL" = "true" ]; then
echo "Using external Redis server at redis://$REDIS_HOST:$REDIS_PORT"
REDIS_PID=""
else
echo "Starting internal Redis server"
redis-server /app/redis.conf &
@@ -49,9 +53,11 @@ terminate() {
echo "Received SIGTERM. Shutting down..."
kill -TERM $NGINX_PID $TASKS_PID $WSS_PID $NEXTJS_PID 2>/dev/null
wait
# kill redis-server last because of logging of other services
kill -TERM $REDIS_PID 2>/dev/null
wait
# kill redis-server last because of logging of other services and only if $REDIS_PID is set
if [ -n "$REDIS_PID" ]; then
kill -TERM $REDIS_PID 2>/dev/null
wait
fi
echo "Shutdown complete."
exit 0
}
@@ -61,4 +67,4 @@ trap terminate TERM INT
# Wait for all processes
wait $NEXTJS_PID
terminate
terminate
+1 -1
View File
@@ -1 +1 @@
[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"tancak","avatar_url":"https://avatars.githubusercontent.com/u/11760749?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/uLine truncated
[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"tancak","avatar_url":"https://avatars.githubusercontent.com/u/11760749?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/uLine truncated
+1 -1
View File
@@ -1 +1 @@
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"Skick","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12619811/medium/0331f1de413a645b4516bc4e037f31bb.jpg"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowLine truncated
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"Skick","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12619811/medium/0331f1de413a645b4516bc4e037f31bb.jpg"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowLine truncated
+3 -3
View File
@@ -17,14 +17,14 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@next/eslint-plugin-next": "16.0.7",
"@next/eslint-plugin-next": "16.0.10",
"eslint-config-prettier": "^10.1.8",
"eslint-config-turbo": "^2.6.1",
"eslint-config-turbo": "^2.6.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^6.1.1",
"typescript-eslint": "^8.46.2"
"typescript-eslint": "^8.48.1"
},
"devDependencies": {
"@homarr/prettier-config": "workspace:^0.1.0",
+1 -1
View File
@@ -11,7 +11,7 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
"prettier": "^3.6.2"
"prettier": "^3.7.4"
},
"devDependencies": {
"@homarr/tsconfig": "workspace:^0.1.0",