Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4607a3ec33 | ||
|
|
ad88c908f0 |
No files matched your search
@@ -33,13 +33,6 @@ body:
|
|||||||
options:
|
options:
|
||||||
# The below comment is used to insert a new version with on-release.yml
|
# The below comment is used to insert a new version with on-release.yml
|
||||||
#NEXT_VERSION#
|
#NEXT_VERSION#
|
||||||
- 1.46.0
|
|
||||||
- 1.45.3
|
|
||||||
- 1.45.2
|
|
||||||
- 1.45.1
|
|
||||||
- 1.45.0
|
|
||||||
- 1.44.0
|
|
||||||
- 1.43.1
|
|
||||||
- 1.43.0
|
- 1.43.0
|
||||||
- 1.42.1
|
- 1.42.1
|
||||||
- 1.42.0
|
- 1.42.0
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
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 build artifact content (amd64 or 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: Prebuilt debian dependencies
|
|
||||||
uses: homarr-labs/homarr/.github/actions/prebuilt-debian@dev
|
|
||||||
id: prebuilt-debian
|
|
||||||
with:
|
|
||||||
architecture: ${{ inputs.architecture }}
|
|
||||||
- name: Start docker container for ${{ inputs.architecture }}
|
|
||||||
run: |
|
|
||||||
docker run --name homarr \
|
|
||||||
-e "SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000" \
|
|
||||||
--detach --rm ${{ inputs.digest }}
|
|
||||||
shell: bash
|
|
||||||
- name: Prepare extraction
|
|
||||||
run: |
|
|
||||||
docker exec homarr cp /etc/nginx/templates/nginx.conf /app && \
|
|
||||||
mkdir -p ${{ runner.temp }}/extraction/${{ inputs.architecture }}
|
|
||||||
shell: bash
|
|
||||||
- name: Extract source from ${{ inputs.architecture }} container (alpine)
|
|
||||||
run: |
|
|
||||||
docker exec homarr tar -czf extraction-alpine.tar.gz -C /app . && \
|
|
||||||
docker cp homarr:/app/extraction-alpine.tar.gz ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-alpine-${{ inputs.architecture }}.tar.gz && \
|
|
||||||
docker exec homarr rm /app/extraction-alpine.tar.gz
|
|
||||||
shell: bash
|
|
||||||
- name: Extract source from ${{ inputs.architecture }} container (debian)
|
|
||||||
run: |
|
|
||||||
docker cp ${{ steps.prebuilt-debian.outputs.path }}/. homarr:/app/build && \
|
|
||||||
docker cp ${{ steps.prebuilt-debian.outputs.path }}/. homarr:/app/node_modules/better-sqlite3/build/Release && \
|
|
||||||
docker exec homarr tar -czf extraction-debian.tar.gz -C /app . && \
|
|
||||||
docker cp homarr:/app/extraction-debian.tar.gz ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-debian-${{ 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 (alpine)
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ inputs.token }}
|
|
||||||
run: gh release upload --repo ${{ inputs.repository }} ${{ inputs.release-tag }} ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-alpine-${{ inputs.architecture }}.tar.gz --clobber
|
|
||||||
shell: bash
|
|
||||||
- name: Add build archive to release (debian)
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ inputs.token }}
|
|
||||||
run: gh release upload --repo ${{ inputs.repository }} ${{ inputs.release-tag }} ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-debian-${{ inputs.architecture }}.tar.gz --clobber
|
|
||||||
shell: bash
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
name: Prebuilt dependencies for debian
|
|
||||||
description: Provides prebuilt dependencies for debian based docker images
|
|
||||||
inputs:
|
|
||||||
architecture:
|
|
||||||
description: Name of architecture, will be used to build docker image (e.g. amd64, arm64)
|
|
||||||
required: true
|
|
||||||
outputs:
|
|
||||||
path:
|
|
||||||
description: Path to extracted prebuilt dependencies
|
|
||||||
value: ${{ runner.temp }}/prebuilts
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build docker image for ${{ inputs.architecture }}
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
push: false
|
|
||||||
load: true
|
|
||||||
context: ./deployments/prebuilt-debian
|
|
||||||
platforms: linux/${{ inputs.architecture }}
|
|
||||||
tags: prebuilt-debian
|
|
||||||
- name: Start docker container for ${{ inputs.architecture }}
|
|
||||||
run: |
|
|
||||||
docker run --name prebuilt-debian \
|
|
||||||
--detach --rm prebuilt-debian
|
|
||||||
shell: bash
|
|
||||||
- name: Extract prebuilt dependencies from ${{ inputs.architecture }} container
|
|
||||||
run: |
|
|
||||||
mkdir -p ${{ runner.temp }}/prebuilts && \
|
|
||||||
docker cp prebuilt-debian:/app/node_modules/better-sqlite3/build/Release/better_sqlite3.node ${{ runner.temp }}/prebuilts/better_sqlite3.node
|
|
||||||
shell: bash
|
|
||||||
- name: Stop ${{ inputs.architecture }} container
|
|
||||||
if: always()
|
|
||||||
run: docker container remove --force --volumes prebuilt-debian
|
|
||||||
shell: bash
|
|
||||||
@@ -15,21 +15,6 @@
|
|||||||
matchUpdateTypes: ["minor", "patch", "pin", "digest"],
|
matchUpdateTypes: ["minor", "patch", "pin", "digest"],
|
||||||
automerge: true,
|
automerge: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// disallow automerge for onlyBuiltDependencies
|
|
||||||
matchPackageNames: [
|
|
||||||
"@tree-sitter-grammars/tree-sitter-yaml",
|
|
||||||
"bcrypt",
|
|
||||||
"better-sqlite3",
|
|
||||||
"cpu-features",
|
|
||||||
"esbuild",
|
|
||||||
"sharp",
|
|
||||||
"ssh2",
|
|
||||||
"tree-sitter",
|
|
||||||
"tree-sitter-json",
|
|
||||||
],
|
|
||||||
automerge: false,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
updateInternalDeps: true,
|
updateInternalDeps: true,
|
||||||
rangeStrategy: "bump",
|
rangeStrategy: "bump",
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ permissions: {}
|
|||||||
jobs:
|
jobs:
|
||||||
approve-automatic-prs:
|
approve-automatic-prs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
|
||||||
if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 # Id of renovate bot and crowdin bot see https://api.github.com/users/homarr-renovate%5Bbot%5D and https://api.github.com/users/homarr-crowdin%5Bbot%5D and https://api.github.com/users/homarr-update-contributors%5Bbot%5D
|
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:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v2
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
name: "[Quality] Code Analysis"
|
name: "[Quality] Code Analysis"
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -23,9 +21,8 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
@@ -39,9 +36,8 @@ jobs:
|
|||||||
|
|
||||||
format:
|
format:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
@@ -51,9 +47,8 @@ jobs:
|
|||||||
|
|
||||||
typecheck:
|
typecheck:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
@@ -63,9 +58,8 @@ jobs:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
@@ -81,10 +75,9 @@ jobs:
|
|||||||
|
|
||||||
e2e:
|
e2e:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
- name: Build docker image
|
- name: Build docker image
|
||||||
@@ -106,9 +99,8 @@ jobs:
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
- name: Copy env
|
- name: Copy env
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
validate-pull-request-title:
|
validate-pull-request-title:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 1
|
|
||||||
steps:
|
steps:
|
||||||
- uses: amannn/action-semantic-pull-request@v6
|
- uses: amannn/action-semantic-pull-request@v6
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
download-crowdin-translations:
|
download-crowdin-translations:
|
||||||
timeout-minutes: 5
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
name: "[Crowdin] Upload translations"
|
name: "[Crowdin] Upload translations"
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -14,11 +12,10 @@ jobs:
|
|||||||
# Don't run this action if the downloaded translations are being pushed
|
# Don't run this action if the downloaded translations are being pushed
|
||||||
if: "!contains(github.event.head_commit.message, 'chore(lang)')"
|
if: "!contains(github.event.head_commit.message, 'chore(lang)')"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Upload Crowdin translations
|
- name: Upload Crowdin translations
|
||||||
uses: crowdin/github-action@v2
|
uses: crowdin/github-action@v2
|
||||||
|
|||||||
@@ -32,13 +32,12 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Create tag and release
|
name: Create tag and release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
|
||||||
env:
|
env:
|
||||||
SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' }}
|
SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' }}
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
|
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
|
||||||
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
|
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
|
||||||
skipped: ${{ env.SKIP_RELEASE }}
|
|
||||||
steps:
|
steps:
|
||||||
- run: echo "Skipping release for workflow_dispatch event"
|
- run: echo "Skipping release for workflow_dispatch event"
|
||||||
if: env.SKIP_RELEASE == 'true'
|
if: env.SKIP_RELEASE == 'true'
|
||||||
@@ -56,16 +55,16 @@ jobs:
|
|||||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
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
|
permission-contents: write # required to commit package.json & changelog changes, merge them to dev and publish the release
|
||||||
|
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v5
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
with:
|
with:
|
||||||
node-version: 24.12.0
|
node-version: 24.10.0
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- run: npm i -g pnpm
|
- run: npm i -g pnpm
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
@@ -113,11 +112,10 @@ jobs:
|
|||||||
name: Build docker image for amd64
|
name: Build docker image for amd64
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
|
||||||
outputs:
|
outputs:
|
||||||
digest: ${{ steps.build.outputs.digest }}
|
digest: ${{ steps.build.outputs.digest }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.release.outputs.git_ref }}
|
ref: ${{ needs.release.outputs.git_ref }}
|
||||||
|
|
||||||
@@ -148,15 +146,15 @@ jobs:
|
|||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
|
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
|
||||||
env:
|
env:
|
||||||
SKIP_ENV_VALIDATION: true
|
SKIP_ENV_VALIDATION: true
|
||||||
|
|
||||||
build-arm64:
|
build-arm64:
|
||||||
name: Build docker image for arm64
|
name: Build docker image for arm64
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
timeout-minutes: 20
|
|
||||||
outputs:
|
outputs:
|
||||||
digest: ${{ steps.build.outputs.digest }}
|
digest: ${{ steps.build.outputs.digest }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.release.outputs.git_ref }}
|
ref: ${{ needs.release.outputs.git_ref }}
|
||||||
|
|
||||||
@@ -187,42 +185,11 @@ jobs:
|
|||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
|
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
|
||||||
env:
|
env:
|
||||||
SKIP_ENV_VALIDATION: true
|
SKIP_ENV_VALIDATION: true
|
||||||
extract-asset-amd64:
|
|
||||||
name: Extract amd64 asset from docker image
|
|
||||||
needs: [release, build-amd64]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 2
|
|
||||||
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
|
|
||||||
timeout-minutes: 2
|
|
||||||
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:
|
publish:
|
||||||
name: Complete deployment and notify
|
name: Complete deployment and notify
|
||||||
needs: [release, build-amd64, build-arm64, extract-asset-amd64, extract-asset-arm64]
|
needs: [release, build-amd64, build-arm64]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
|
||||||
env:
|
env:
|
||||||
NEXT_VERSION: ${{ needs.release.outputs.version }}
|
NEXT_VERSION: ${{ needs.release.outputs.version }}
|
||||||
DEPLOY_LATEST: ${{ github.ref_name == 'main' }}
|
DEPLOY_LATEST: ${{ github.ref_name == 'main' }}
|
||||||
@@ -255,13 +222,6 @@ jobs:
|
|||||||
${{ env.GHCR_REPO }}@${{ needs.build-amd64.outputs.digest }} \
|
${{ env.GHCR_REPO }}@${{ needs.build-amd64.outputs.digest }} \
|
||||||
${{ env.GHCR_REPO }}@${{ needs.build-arm64.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
|
- name: Discord notification
|
||||||
env:
|
env:
|
||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
create-and-merge-pr:
|
create-and-merge-pr:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
|
||||||
steps:
|
steps:
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
if: ${{ github.events.inputs.send-notifications }}
|
if: ${{ github.events.inputs.send-notifications }}
|
||||||
@@ -28,7 +27,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
args: "Automatic release has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
args: "Automatic release has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Get Next Version
|
- name: Get Next Version
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
name: "[Deployments] Validate prebuilt debian dependencies"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: ["*"]
|
|
||||||
paths: [".github/actions/prebuilt-debian/**", "deployments/prebuilt-debian/**"]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prebuilt-debian-validate-amd64:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 5
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- name: Validate prebuilt dependencies for amd64
|
|
||||||
id: validate-amd64
|
|
||||||
uses: ./.github/actions/prebuilt-debian
|
|
||||||
with:
|
|
||||||
architecture: amd64
|
|
||||||
|
|
||||||
- name: Check extracted files for amd64
|
|
||||||
run: |
|
|
||||||
if [ ! -f "${{ steps.validate-amd64.outputs.path }}/better_sqlite3.node" ]; then
|
|
||||||
echo "better_sqlite3.node not found for amd64!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
prebuilt-debian-validate-arm64:
|
|
||||||
runs-on: ubuntu-24.04-
|
|
||||||
timeout-minutes: 5
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- name: Validate prebuilt dependencies for arm64
|
|
||||||
id: validate-arm64
|
|
||||||
uses: ./.github/actions/prebuilt-debian
|
|
||||||
with:
|
|
||||||
architecture: arm64
|
|
||||||
|
|
||||||
- name: Check extracted files for arm64
|
|
||||||
run: |
|
|
||||||
if [ ! -f "${{ steps.validate-arm64.outputs.path }}/better_sqlite3.node" ]; then
|
|
||||||
echo "better_sqlite3.node not found for arm64!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -11,9 +11,8 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
renovate-validate:
|
renovate-validate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
- run: |
|
- run: |
|
||||||
npx --yes --package renovate -- \
|
npx --yes --package renovate -- \
|
||||||
renovate-config-validator --strict .github/renovate.json5
|
renovate-config-validator --strict .github/renovate.json5
|
||||||
@@ -8,7 +8,6 @@ jobs:
|
|||||||
trigger-docs-release:
|
trigger-docs-release:
|
||||||
name: Trigger Documentation Release
|
name: Trigger Documentation Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
|
||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
@@ -40,7 +39,6 @@ jobs:
|
|||||||
update-bug-report-template:
|
update-bug-report-template:
|
||||||
name: Update Bug Report Template
|
name: Update Bug Report Template
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
|
||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
@@ -51,7 +49,7 @@ jobs:
|
|||||||
permission-contents: write # required to commit to branch
|
permission-contents: write # required to commit to branch
|
||||||
permission-pull-requests: write # required to create pr & enable automerge
|
permission-pull-requests: write # required to create pr & enable automerge
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.obtainToken.outputs.token }}
|
token: ${{ steps.obtainToken.outputs.token }}
|
||||||
- name: Setup
|
- name: Setup
|
||||||
@@ -69,7 +67,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: create-pull-request
|
id: create-pull-request
|
||||||
uses: peter-evans/create-pull-request@v8
|
uses: peter-evans/create-pull-request@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.obtainToken.outputs.token }}
|
token: ${{ steps.obtainToken.outputs.token }}
|
||||||
branch: update-bug-report-template
|
branch: update-bug-report-template
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ jobs:
|
|||||||
if: ${{ !startsWith(github.head_ref, 'renovate/') }}
|
if: ${{ !startsWith(github.head_ref, 'renovate/') }}
|
||||||
name: Skip Stability Days
|
name: Skip Stability Days
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 1
|
|
||||||
steps:
|
steps:
|
||||||
- name: Add status check
|
- name: Add status check
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -9,12 +9,9 @@ env:
|
|||||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
CROWDIN_TOKEN: "${{ secrets.CROWDIN_UPDATE_CONTRIBUTORS_TOKEN }}"
|
CROWDIN_TOKEN: "${{ secrets.CROWDIN_UPDATE_CONTRIBUTORS_TOKEN }}"
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
jobs:
|
jobs:
|
||||||
update-contributors:
|
update-contributors:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [22]
|
node-version: [22]
|
||||||
@@ -29,13 +26,13 @@ jobs:
|
|||||||
permission-pull-requests: write # required to create pr & enable automerge
|
permission-pull-requests: write # required to create pr & enable automerge
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@@ -54,7 +51,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: create-pull-request
|
id: create-pull-request
|
||||||
uses: peter-evans/create-pull-request@v8
|
uses: peter-evans/create-pull-request@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.obtainToken.outputs.token }}
|
token: ${{ steps.obtainToken.outputs.token }}
|
||||||
branch: update-contributors
|
branch: update-contributors
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ jobs:
|
|||||||
group: update-integration
|
group: update-integration
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
|
||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
@@ -28,7 +27,7 @@ jobs:
|
|||||||
permission-contents: write # required to commit to branch
|
permission-contents: write # required to commit to branch
|
||||||
permission-pull-requests: write # required to create pr & enable automerge
|
permission-pull-requests: write # required to create pr & enable automerge
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||||
- name: Setup
|
- name: Setup
|
||||||
@@ -45,7 +44,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: create-pull-request
|
id: create-pull-request
|
||||||
uses: peter-evans/create-pull-request@v8
|
uses: peter-evans/create-pull-request@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.obtainToken.outputs.token }}
|
token: ${{ steps.obtainToken.outputs.token }}
|
||||||
branch: update-integrations-readme
|
branch: update-integrations-readme
|
||||||
|
|||||||
@@ -3,4 +3,3 @@
|
|||||||
# @link https://docs.expo.dev/guides/monorepos/#common-issues
|
# @link https://docs.expo.dev/guides/monorepos/#common-issues
|
||||||
node-linker=hoisted
|
node-linker=hoisted
|
||||||
strict-peer-dependencies=false
|
strict-peer-dependencies=false
|
||||||
engine-strict=true
|
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
{
|
{
|
||||||
"successComment": false,
|
"successComment": false,
|
||||||
"failComment": false,
|
"failComment": false,
|
||||||
"draftRelease": true,
|
|
||||||
"releaseBodyTemplate": "<%= _.truncate(nextRelease.notes, { 'length': 124000, 'omission': '' }) %>"
|
"releaseBodyTemplate": "<%= _.truncate(nextRelease.notes, { 'length': 124000, 'omission': '' }) %>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
-209
@@ -1,212 +1,3 @@
|
|||||||
## [1.47.0](https://github.com/homarr-labs/homarr/compare/v1.46.0...v1.47.0) (2025-12-19)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* [#4452](https://github.com/homarr-labs/homarr/issues/4452) improve calendar design ([#4461](https://github.com/homarr-labs/homarr/issues/4461)) ([08e9f7d](https://github.com/homarr-labs/homarr/commit/08e9f7d8d85475788534b46e6cceb40a7231c09d))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **boards:** selection menu not shown to users without change access ([#4689](https://github.com/homarr-labs/homarr/issues/4689)) ([a0a11e3](https://github.com/homarr-labs/homarr/commit/a0a11e35707513fee45551e09c26ff633f636c32))
|
|
||||||
* **deps:** update dependency dns-caching to ^0.2.9 ([#4648](https://github.com/homarr-labs/homarr/issues/4648)) ([6deab2e](https://github.com/homarr-labs/homarr/commit/6deab2ecec2dc4f5192b53213cb755a9853eb7c0))
|
|
||||||
* **deps:** update dependency drizzle-orm to ^0.45.1 ([#4663](https://github.com/homarr-labs/homarr/issues/4663)) ([f635009](https://github.com/homarr-labs/homarr/commit/f63500961f11589a972a41dde9e4117b1f25141c))
|
|
||||||
* **deps:** update dependency fast-xml-parser to ^5.3.3 ([#4683](https://github.com/homarr-labs/homarr/issues/4683)) ([611b65e](https://github.com/homarr-labs/homarr/commit/611b65e8bd420aaad580bf35071b651093623c95))
|
|
||||||
* **deps:** update dependency isomorphic-dompurify to ^2.34.0 ([#4650](https://github.com/homarr-labs/homarr/issues/4650)) ([5d5c18b](https://github.com/homarr-labs/homarr/commit/5d5c18b3649d5fa1b2b933d443ccd5abcbb84e3a))
|
|
||||||
* **deps:** update dependency jotai to ^2.16.0 ([#4655](https://github.com/homarr-labs/homarr/issues/4655)) ([4aef85b](https://github.com/homarr-labs/homarr/commit/4aef85ba60f4978da5a0b13c08e1799949fe4497))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.16 ([#4640](https://github.com/homarr-labs/homarr/issues/4640)) ([ada5597](https://github.com/homarr-labs/homarr/commit/ada5597cf3ab0bafb04e9176b431d257b36d1dc8))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.17 ([#4642](https://github.com/homarr-labs/homarr/issues/4642)) ([c09da85](https://github.com/homarr-labs/homarr/commit/c09da85f3a8450a70991ff0656228a9aa1364f89))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.18 ([#4653](https://github.com/homarr-labs/homarr/issues/4653)) ([4bd142e](https://github.com/homarr-labs/homarr/commit/4bd142e09c666cc84c67c93e2202e571565b766c))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.19 ([#4669](https://github.com/homarr-labs/homarr/issues/4669)) ([8ed771d](https://github.com/homarr-labs/homarr/commit/8ed771d30073809b08a6ace2c29b3de2675e37d1))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.20 ([#4684](https://github.com/homarr-labs/homarr/issues/4684)) ([703c8cc](https://github.com/homarr-labs/homarr/commit/703c8ccc060bb1775a72f405da1e169926b1ce5d))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.21 ([#4695](https://github.com/homarr-labs/homarr/issues/4695)) ([7f6eaa0](https://github.com/homarr-labs/homarr/commit/7f6eaa0b1f397a37b6acd15e0ccfea75ee2e0161))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.22 ([#4700](https://github.com/homarr-labs/homarr/issues/4700)) ([391d725](https://github.com/homarr-labs/homarr/commit/391d725191563ba5048593c14baf077a924cea44))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.23 ([#4701](https://github.com/homarr-labs/homarr/issues/4701)) ([db337b2](https://github.com/homarr-labs/homarr/commit/db337b2525762c96acd815bd4fdacfaabff5f0cf))
|
|
||||||
* **deps:** update dependency next-intl to v4.6.0 ([#4691](https://github.com/homarr-labs/homarr/issues/4691)) ([1ff6fac](https://github.com/homarr-labs/homarr/commit/1ff6fac71bb9187d0eb703a7b6543daf8861efe8))
|
|
||||||
* **deps:** update dependency sass to ^1.94.3 ([#4646](https://github.com/homarr-labs/homarr/issues/4646)) ([7e91d12](https://github.com/homarr-labs/homarr/commit/7e91d12439826eee76f118448c583294c3c46b42))
|
|
||||||
* **deps:** update dependency sass to ^1.95.0 ([#4647](https://github.com/homarr-labs/homarr/issues/4647)) ([eb760da](https://github.com/homarr-labs/homarr/commit/eb760da8b30583575fb638995fc2f2acbd5b2961))
|
|
||||||
* **deps:** update dependency sass to ^1.95.1 ([#4661](https://github.com/homarr-labs/homarr/issues/4661)) ([e327c66](https://github.com/homarr-labs/homarr/commit/e327c660b239840c4929f536eff3096342cc3433))
|
|
||||||
* **deps:** update dependency sass to ^1.96.0 ([#4668](https://github.com/homarr-labs/homarr/issues/4668)) ([bdfc150](https://github.com/homarr-labs/homarr/commit/bdfc1501152450a1546962331037a6fc1c782e4e))
|
|
||||||
* **deps:** update dependency swagger-ui-react to ^5.31.0 ([#4672](https://github.com/homarr-labs/homarr/issues/4672)) ([4d593f5](https://github.com/homarr-labs/homarr/commit/4d593f5b9d1f47ae2368f42131e8ef8d32f73fc9))
|
|
||||||
* **deps:** update dependency typescript-eslint to ^8.49.0 ([#4645](https://github.com/homarr-labs/homarr/issues/4645)) ([220cb66](https://github.com/homarr-labs/homarr/commit/220cb669a13fead953ff42040b39e06d1dcc1fe2))
|
|
||||||
* **deps:** update mantine monorepo to ^8.3.10 ([#4651](https://github.com/homarr-labs/homarr/issues/4651)) ([d0652f7](https://github.com/homarr-labs/homarr/commit/d0652f7cf5ac0e63a61adcfe43e53c4ed130e8fc))
|
|
||||||
* **deps:** update react monorepo to v19.2.2 ([#4676](https://github.com/homarr-labs/homarr/issues/4676)) ([82e79f2](https://github.com/homarr-labs/homarr/commit/82e79f26274a289e1ce4f9331b6ba16f00f5a86c))
|
|
||||||
* **deps:** update react monorepo to v19.2.3 ([#4680](https://github.com/homarr-labs/homarr/issues/4680)) ([4c71856](https://github.com/homarr-labs/homarr/commit/4c7185646d9d061d1aebf9194f9ef2f810fc3384))
|
|
||||||
* **deps:** update testcontainers-node monorepo to ^11.10.0 ([#4659](https://github.com/homarr-labs/homarr/issues/4659)) ([c880ce9](https://github.com/homarr-labs/homarr/commit/c880ce9bd99a7056f20d450275fcbc37f9372f63))
|
|
||||||
* **deps:** update trpc monorepo to ^11.8.0 ([#4697](https://github.com/homarr-labs/homarr/issues/4697)) ([1f44845](https://github.com/homarr-labs/homarr/commit/1f44845df489bb33bcfe943c6a05c0f157037ed6))
|
|
||||||
* **downloads:** improve progress to only show 100 percent if fully completed ([#4621](https://github.com/homarr-labs/homarr/issues/4621)) ([1062745](https://github.com/homarr-labs/homarr/commit/10627453d5b5a17991bbe9b8d4bc23981b366c39))
|
|
||||||
* **iframe-widget:** permission selection not applying to any origin ([#4616](https://github.com/homarr-labs/homarr/issues/4616)) ([479f083](https://github.com/homarr-labs/homarr/commit/479f083ab8ec405d1b8296b32a20262f6192a1f9))
|
|
||||||
* **invites:** creation with custom expiration not working ([#4677](https://github.com/homarr-labs/homarr/issues/4677)) ([423da9e](https://github.com/homarr-labs/homarr/commit/423da9eeb88510a123bdd2447dc97b54c5f1dec4))
|
|
||||||
* reference to prebuilt-debian action does not work ([47d7458](https://github.com/homarr-labs/homarr/commit/47d7458143d8801947cae1985745fa827b247dd1))
|
|
||||||
* **stock-price:** use previous-close for change calculation ([#4674](https://github.com/homarr-labs/homarr/issues/4674)) ([17fc7a0](https://github.com/homarr-labs/homarr/commit/17fc7a043b4adbed9f14408e23c95ec5fba0c8b8))
|
|
||||||
* **tdarr:** array parse error ([#4678](https://github.com/homarr-labs/homarr/issues/4678)) ([d86af07](https://github.com/homarr-labs/homarr/commit/d86af072bf136d76b02f3efa2c15520a4f63974e))
|
|
||||||
|
|
||||||
## [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
|
|
||||||
|
|
||||||
* **deps:** update dependency chroma-js to ^3.1.4 ([#4570](https://github.com/homarr-labs/homarr/issues/4570)) ([d6fc46d](https://github.com/homarr-labs/homarr/commit/d6fc46df3b5e2dd79ff6f9a3dd3a3e2050a0eeef))
|
|
||||||
* **deps:** update dependency chroma-js to ^3.2.0 ([#4585](https://github.com/homarr-labs/homarr/issues/4585)) ([b2c46d3](https://github.com/homarr-labs/homarr/commit/b2c46d33d6cc769e89e208fd29d3a1d143cf74c7))
|
|
||||||
* **deps:** update dependency isomorphic-dompurify to ^2.33.0 ([#4581](https://github.com/homarr-labs/homarr/issues/4581)) ([ca01a31](https://github.com/homarr-labs/homarr/commit/ca01a31fe5cbce53eb0e6bd1a943320c092161ef))
|
|
||||||
* **deps:** update dependency next-intl to v4.5.6 ([#4571](https://github.com/homarr-labs/homarr/issues/4571)) ([3062dda](https://github.com/homarr-labs/homarr/commit/3062ddafbe0bc1dbac9259af4036f0fa32d85237))
|
|
||||||
* **deps:** update dependency sass to ^1.94.2 ([#4572](https://github.com/homarr-labs/homarr/issues/4572)) ([7a8c50f](https://github.com/homarr-labs/homarr/commit/7a8c50fa4c5224dd74af3c17f56e2e5b8d6a4f43))
|
|
||||||
* **deps:** update dependency superjson to v2.2.6 ([#4573](https://github.com/homarr-labs/homarr/issues/4573)) ([deb5e9d](https://github.com/homarr-labs/homarr/commit/deb5e9d96092069e296ba473eaad58367b516f85))
|
|
||||||
* **deps:** update dependency swagger-ui-react to ^5.30.3 ([#4574](https://github.com/homarr-labs/homarr/issues/4574)) ([d2c1dc3](https://github.com/homarr-labs/homarr/commit/d2c1dc3cc1dd72fc9fc8ae9613467fa0b0b8d75a))
|
|
||||||
* **deps:** update dependency zod to ^4.1.13 ([#4575](https://github.com/homarr-labs/homarr/issues/4575)) ([879a77f](https://github.com/homarr-labs/homarr/commit/879a77ff2a81b0d0a17d1487eaf6d8539a955301))
|
|
||||||
* **deps:** update mantine monorepo to ^8.3.9 ([#4576](https://github.com/homarr-labs/homarr/issues/4576)) ([d5d59dd](https://github.com/homarr-labs/homarr/commit/d5d59dd2561f0be2f4f4c689f33c6a9a7840a21c))
|
|
||||||
* **deps:** update nextjs monorepo to v16.0.5 ([#4577](https://github.com/homarr-labs/homarr/issues/4577)) ([16f12d9](https://github.com/homarr-labs/homarr/commit/16f12d98e6cdae50fe2989db0911cb1350fcb93b))
|
|
||||||
* **deps:** update tanstack-query monorepo ([#4578](https://github.com/homarr-labs/homarr/issues/4578)) ([59bcbc8](https://github.com/homarr-labs/homarr/commit/59bcbc8b4de4a0a8cee8adf6336c7c10477c766a))
|
|
||||||
* **deps:** update testcontainers-node monorepo to ^11.9.0 ([#4583](https://github.com/homarr-labs/homarr/issues/4583)) ([10bdd74](https://github.com/homarr-labs/homarr/commit/10bdd74b2b287cb08ec0926147ac0e7fcfd492bb))
|
|
||||||
* **deps:** update trpc monorepo to ^11.7.2 ([#4579](https://github.com/homarr-labs/homarr/issues/4579)) ([3453482](https://github.com/homarr-labs/homarr/commit/3453482ed43644025c39dee8e20549e1beb86be0))
|
|
||||||
* **security:** rce vulnerability in nextjs ([b40deba](https://github.com/homarr-labs/homarr/commit/b40deba9ff1a3d629b6027565378d9f3ad2f1980))
|
|
||||||
|
|
||||||
## [1.45.0](https://github.com/homarr-labs/homarr/compare/v1.44.0...v1.45.0) (2025-11-28)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **docker:** hide containers with label homarr.hide ([#4506](https://github.com/homarr-labs/homarr/issues/4506)) ([60ab98a](https://github.com/homarr-labs/homarr/commit/60ab98ae36abe712c825f7828394d3e3e98c888a))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Add fallback when no icon in Minecraft Server widget ([#4544](https://github.com/homarr-labs/homarr/issues/4544)) ([#4545](https://github.com/homarr-labs/homarr/issues/4545)) ([99949fe](https://github.com/homarr-labs/homarr/commit/99949fe095ac5a3616c8e9c1964d7eb253c9f826))
|
|
||||||
* **deps:** update dependency @tanstack/react-query to ^5.90.10 ([#4523](https://github.com/homarr-labs/homarr/issues/4523)) ([e87170a](https://github.com/homarr-labs/homarr/commit/e87170ae6e56cdbeef52e943353060329c39bdc9))
|
|
||||||
* **deps:** update dependency glob to ^11.1.0 ([#4529](https://github.com/homarr-labs/homarr/issues/4529)) ([24c9854](https://github.com/homarr-labs/homarr/commit/24c985424ecc795eb96b56881a9b00c0c37386c1))
|
|
||||||
* **deps:** update dependency glob to v12 ([#4530](https://github.com/homarr-labs/homarr/issues/4530)) ([18a6bfd](https://github.com/homarr-labs/homarr/commit/18a6bfd69d86de8c58dbfe6dc8b8fab78b2b5ee6))
|
|
||||||
* **deps:** update dependency next-intl to v4.5.4 ([#4540](https://github.com/homarr-labs/homarr/issues/4540)) ([77a86ea](https://github.com/homarr-labs/homarr/commit/77a86ead41c0d098987561249203667ac4d88177))
|
|
||||||
* **deps:** update dependency next-intl to v4.5.5 ([#4542](https://github.com/homarr-labs/homarr/issues/4542)) ([2bc6964](https://github.com/homarr-labs/homarr/commit/2bc6964f7efb5203f626d2f07b847dffd53191f0))
|
|
||||||
* **deps:** update dependency sass to ^1.94.1 ([#4531](https://github.com/homarr-labs/homarr/issues/4531)) ([292e36f](https://github.com/homarr-labs/homarr/commit/292e36fec5acff9b1d143f19e18ea170c87fa0d0))
|
|
||||||
* **deps:** update dependency zod-validation-error to v5 ([#4437](https://github.com/homarr-labs/homarr/issues/4437)) ([9a3feb4](https://github.com/homarr-labs/homarr/commit/9a3feb404c0c284b4f33fce12d87a469741a6ebd))
|
|
||||||
* **security:** restrict modification of entrypoint script ([#4555](https://github.com/homarr-labs/homarr/issues/4555)) ([524e1a4](https://github.com/homarr-labs/homarr/commit/524e1a485be56dbebf8f71d4546bc65672737f30))
|
|
||||||
|
|
||||||
## [1.44.0](https://github.com/homarr-labs/homarr/compare/v1.43.3...v1.44.0) (2025-11-21)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **pi-hole:** support no-auth ([#4480](https://github.com/homarr-labs/homarr/issues/4480)) ([c89e35e](https://github.com/homarr-labs/homarr/commit/c89e35e13c794bbdf98748c00644b86f7f5dbe68))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **deps:** update dependency @tanstack/react-query to ^5.90.8 ([#4494](https://github.com/homarr-labs/homarr/issues/4494)) ([a5b6203](https://github.com/homarr-labs/homarr/commit/a5b6203be6ac3c2308614f10ef6a17590917e2c9))
|
|
||||||
* **deps:** update dependency @tanstack/react-query to ^5.90.9 ([#4508](https://github.com/homarr-labs/homarr/issues/4508)) ([b3db5d5](https://github.com/homarr-labs/homarr/commit/b3db5d5f7bc2d146f0b2c230a52e7bd4efad3bcb))
|
|
||||||
* **deps:** update dependency @tanstack/react-query-next-experimental to ^5.91.0 ([#4505](https://github.com/homarr-labs/homarr/issues/4505)) ([8ea6cff](https://github.com/homarr-labs/homarr/commit/8ea6cffb537ca54a413c583ab8b8fd24a52185be))
|
|
||||||
* **deps:** update dependency drizzle-kit to ^0.31.7 ([#4507](https://github.com/homarr-labs/homarr/issues/4507)) ([b4f9fb8](https://github.com/homarr-labs/homarr/commit/b4f9fb8b46a8f74e2d7896dfe98e80618cfbcde4))
|
|
||||||
* **deps:** update dependency fast-xml-parser to ^5.3.2 ([#4510](https://github.com/homarr-labs/homarr/issues/4510)) ([5e57a49](https://github.com/homarr-labs/homarr/commit/5e57a4906bcedc53d220e983827293f4a699c425))
|
|
||||||
* **deps:** update dependency next-intl to v4.5.1 ([#4484](https://github.com/homarr-labs/homarr/issues/4484)) ([06942ec](https://github.com/homarr-labs/homarr/commit/06942ecbcd245f5540f2960f437ca2955a5ed0c3))
|
|
||||||
* **deps:** update dependency next-intl to v4.5.3 ([#4503](https://github.com/homarr-labs/homarr/issues/4503)) ([0642773](https://github.com/homarr-labs/homarr/commit/064277373cacd3309d8da3779d5451d195da7f81))
|
|
||||||
* **deps:** update dependency sass to ^1.94.0 ([#4482](https://github.com/homarr-labs/homarr/issues/4482)) ([19ecede](https://github.com/homarr-labs/homarr/commit/19ecedea4370924d47545ca8d9d46a0b7c016cc9))
|
|
||||||
* **deps:** update mantine monorepo to ^8.3.8 ([#4516](https://github.com/homarr-labs/homarr/issues/4516)) ([ded4e72](https://github.com/homarr-labs/homarr/commit/ded4e72e5407b0076495a06111f3b04445f27276))
|
|
||||||
* **deps:** update nextjs monorepo to v16.0.3 ([#4504](https://github.com/homarr-labs/homarr/issues/4504)) ([7d0c1a1](https://github.com/homarr-labs/homarr/commit/7d0c1a14fdf1731ff44e3d00793cf4cd1c3e9b1f))
|
|
||||||
* **deps:** update testcontainers-node monorepo to ^11.8.1 ([#4486](https://github.com/homarr-labs/homarr/issues/4486)) ([64657d8](https://github.com/homarr-labs/homarr/commit/64657d831cbe28a59708b280b07889b9f0232743))
|
|
||||||
* **deps:** update turbo monorepo to ^2.6.1 ([#4483](https://github.com/homarr-labs/homarr/issues/4483)) ([cd124d6](https://github.com/homarr-labs/homarr/commit/cd124d6e8392eef5df0092481ec3cc26599f2902))
|
|
||||||
* **iframe:** allow clicking links within ([#4521](https://github.com/homarr-labs/homarr/issues/4521)) ([5a418d7](https://github.com/homarr-labs/homarr/commit/5a418d73bdb07cf3d3270983b6881f2de8d58ad7))
|
|
||||||
* **media-requests:** incorrect availability mapping ([#4520](https://github.com/homarr-labs/homarr/issues/4520)) ([d9cc35b](https://github.com/homarr-labs/homarr/commit/d9cc35b9859a14f44c4e4b13c6518287f8d18e54))
|
|
||||||
|
|
||||||
## [1.43.3](https://github.com/homarr-labs/homarr/compare/v1.43.2...v1.43.3) (2025-11-14)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **deps:** update dependency @ctrl/qbittorrent to ^9.11.0 ([#4456](https://github.com/homarr-labs/homarr/issues/4456)) ([ddfed18](https://github.com/homarr-labs/homarr/commit/ddfed18acf44596dba15ea0ef56233c4936f9775))
|
|
||||||
* **deps:** update dependency @tanstack/react-query to ^5.90.7 ([#4451](https://github.com/homarr-labs/homarr/issues/4451)) ([7bc2f87](https://github.com/homarr-labs/homarr/commit/7bc2f87f31f1babe691fa4a086a3897977f2d871))
|
|
||||||
* **deps:** update dependency fast-xml-parser to ^5.3.1 ([#4435](https://github.com/homarr-labs/homarr/issues/4435)) ([65f2e82](https://github.com/homarr-labs/homarr/commit/65f2e825fdb9e5e79b814c9b49f663d1afac784d))
|
|
||||||
* **deps:** update dependency fastify to ^5.6.2 ([#4471](https://github.com/homarr-labs/homarr/issues/4471)) ([e7071bf](https://github.com/homarr-labs/homarr/commit/e7071bf2bf6a45ff760cd3e7bd06382d2ed66cdc))
|
|
||||||
* **deps:** update dependency next-intl to v4.5.0 ([#4462](https://github.com/homarr-labs/homarr/issues/4462)) ([51ce5da](https://github.com/homarr-labs/homarr/commit/51ce5da45bdccf061e593097ed751cf09d663437))
|
|
||||||
* **deps:** update dependency swagger-ui-react to ^5.30.2 ([#4442](https://github.com/homarr-labs/homarr/issues/4442)) ([d3edfcf](https://github.com/homarr-labs/homarr/commit/d3edfcf3dca3d34afd5ef8ec21599cd08fde5092))
|
|
||||||
* **deps:** update mantine monorepo to ^8.3.7 ([#4472](https://github.com/homarr-labs/homarr/issues/4472)) ([8a87dba](https://github.com/homarr-labs/homarr/commit/8a87dbad59786fa9789dd3efb8d9424bb950e977))
|
|
||||||
* **deps:** update testcontainers-node monorepo to ^11.8.0 ([#4450](https://github.com/homarr-labs/homarr/issues/4450)) ([f648af0](https://github.com/homarr-labs/homarr/commit/f648af0e2b2b5d5f8180402c2734ea0c04559daa))
|
|
||||||
* **stock-price:** ignore missing data points in price history ([#4429](https://github.com/homarr-labs/homarr/issues/4429)) ([c30c98b](https://github.com/homarr-labs/homarr/commit/c30c98b153fbec21551e45aedf83e0ae29d6e773))
|
|
||||||
* **users:** next button in create form always disabled ([#4443](https://github.com/homarr-labs/homarr/issues/4443)) ([1c1f8a4](https://github.com/homarr-labs/homarr/commit/1c1f8a48d259c65ebeeec6856e1fe339defb4aaf))
|
|
||||||
|
|
||||||
## [1.43.2](https://github.com/homarr-labs/homarr/compare/v1.43.1...v1.43.2) (2025-11-07)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **deps:** update dependency @ctrl/deluge to ^7.5.0 ([#4407](https://github.com/homarr-labs/homarr/issues/4407)) ([57d1a91](https://github.com/homarr-labs/homarr/commit/57d1a9186653dd4c327b360a3bdaa78fd8e1187b))
|
|
||||||
* **deps:** update dependency @gitbeaker/rest to ^43.7.0 ([#4386](https://github.com/homarr-labs/homarr/issues/4386)) ([dcf960f](https://github.com/homarr-labs/homarr/commit/dcf960fa97011c24475f303d587152b42eabec45))
|
|
||||||
* **deps:** update dependency @gitbeaker/rest to ^43.8.0 ([#4425](https://github.com/homarr-labs/homarr/issues/4425)) ([4516f27](https://github.com/homarr-labs/homarr/commit/4516f27fa69445e5aec16760e1f6ed8080bc8fff))
|
|
||||||
* **deps:** update dependency @jellyfin/sdk to ^0.13.0 ([#4399](https://github.com/homarr-labs/homarr/issues/4399)) ([4c4e901](https://github.com/homarr-labs/homarr/commit/4c4e901e7d38faf83a4725b7cee5526b0b223dc3))
|
|
||||||
* **deps:** update dependency @octokit/auth-app to ^8.1.2 ([#4412](https://github.com/homarr-labs/homarr/issues/4412)) ([018a33b](https://github.com/homarr-labs/homarr/commit/018a33b07812fa59f1469ce8b4baa7b1d55c6103))
|
|
||||||
* **deps:** update dependency @tanstack/react-query to ^5.90.6 ([#4427](https://github.com/homarr-labs/homarr/issues/4427)) ([5f9054b](https://github.com/homarr-labs/homarr/commit/5f9054b162daf974baafdbee9f844b3a860cbdd0))
|
|
||||||
* **deps:** update dependency dayjs to ^1.11.19 ([#4414](https://github.com/homarr-labs/homarr/issues/4414)) ([97cbade](https://github.com/homarr-labs/homarr/commit/97cbade7135cb06e0780dd1b448c78c8802794ea))
|
|
||||||
* **deps:** update dependency drizzle-kit to ^0.31.6 ([#4397](https://github.com/homarr-labs/homarr/issues/4397)) ([59434ee](https://github.com/homarr-labs/homarr/commit/59434ee33ae16e06f96e2f07cf85337c313c73f6))
|
|
||||||
* **deps:** update dependency jotai to ^2.15.1 ([#4426](https://github.com/homarr-labs/homarr/issues/4426)) ([07af9d8](https://github.com/homarr-labs/homarr/commit/07af9d8673ea9573bc436626cd84d11980d023bc))
|
|
||||||
* **deps:** update dependency octokit to ^5.0.5 ([#4413](https://github.com/homarr-labs/homarr/issues/4413)) ([186e3e4](https://github.com/homarr-labs/homarr/commit/186e3e445dfe6b4fb1aeda3830ae1f260b605882))
|
|
||||||
* **deps:** update dependency sass to ^1.93.3 ([#4421](https://github.com/homarr-labs/homarr/issues/4421)) ([f216427](https://github.com/homarr-labs/homarr/commit/f2164272a5ca9b51bdf9110664ed5a40b87b60f6))
|
|
||||||
* **deps:** update dependency superjson to v2.2.5 ([#4398](https://github.com/homarr-labs/homarr/issues/4398)) ([19e25a0](https://github.com/homarr-labs/homarr/commit/19e25a095fb0252e8f2a459e0a97d44a37d2188a))
|
|
||||||
* **deps:** update dependency swagger-ui-react to ^5.30.0 ([#4391](https://github.com/homarr-labs/homarr/issues/4391)) ([b51e285](https://github.com/homarr-labs/homarr/commit/b51e285c92ef707076f321f0ce1a15d5cbd5de7d))
|
|
||||||
* **deps:** update dependency swagger-ui-react to ^5.30.1 ([#4418](https://github.com/homarr-labs/homarr/issues/4418)) ([d9b14db](https://github.com/homarr-labs/homarr/commit/d9b14db6a1af515796951a1db670431d10f81eaf))
|
|
||||||
* **deps:** update dependency tsdav to ^2.1.6 ([#4417](https://github.com/homarr-labs/homarr/issues/4417)) ([160c20a](https://github.com/homarr-labs/homarr/commit/160c20a9a6f850f75537e246f42e93f9a2ed79a0))
|
|
||||||
* **deps:** update mantine monorepo to ^8.3.6 ([#4403](https://github.com/homarr-labs/homarr/issues/4403)) ([9e2bbb8](https://github.com/homarr-labs/homarr/commit/9e2bbb8a3f2db95c5598e5dca993bf6cd4ebc127))
|
|
||||||
* **deps:** update nextjs monorepo to v16 (major) ([#4363](https://github.com/homarr-labs/homarr/issues/4363)) ([6ce23a6](https://github.com/homarr-labs/homarr/commit/6ce23a6e971b064292970fc4df90cc69cdb59a95))
|
|
||||||
* **deps:** update tiptap monorepo to v2.27.0 ([#4404](https://github.com/homarr-labs/homarr/issues/4404)) ([f4caddf](https://github.com/homarr-labs/homarr/commit/f4caddfd564553014854347eb6dd14cc800eb027))
|
|
||||||
* **deps:** update tiptap monorepo to v2.27.1 ([#4405](https://github.com/homarr-labs/homarr/issues/4405)) ([d34f9e5](https://github.com/homarr-labs/homarr/commit/d34f9e5b3ef999559a4c5f1165488fe4f8828a83))
|
|
||||||
* **deps:** update trpc monorepo to ^11.7.1 ([#4396](https://github.com/homarr-labs/homarr/issues/4396)) ([5aca5d1](https://github.com/homarr-labs/homarr/commit/5aca5d1b0a76ab4cf0ed27dac62183c766ad398a))
|
|
||||||
* **deps:** update turbo monorepo to ^2.6.0 ([#4422](https://github.com/homarr-labs/homarr/issues/4422)) ([742ef66](https://github.com/homarr-labs/homarr/commit/742ef66e3cf56fd351c8dee2d3dd05cd639e51fa))
|
|
||||||
|
|
||||||
## [1.43.1](https://github.com/homarr-labs/homarr/compare/v1.43.0...v1.43.1) (2025-10-31)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **deps:** update dependency @auth/core to ^0.41.1 ([#4387](https://github.com/homarr-labs/homarr/issues/4387)) ([747bb92](https://github.com/homarr-labs/homarr/commit/747bb922ccf0508bd00c37faeab29e28876577b1))
|
|
||||||
* **deps:** update dependency @auth/drizzle-adapter to ^1.11.1 ([#4388](https://github.com/homarr-labs/homarr/issues/4388)) ([2fe3656](https://github.com/homarr-labs/homarr/commit/2fe36564c26c33ae2625eb22ef4503c6dd8ebcf9))
|
|
||||||
* **deps:** update dependency @jellyfin/sdk to ^0.12.0 ([#4359](https://github.com/homarr-labs/homarr/issues/4359)) ([554d19a](https://github.com/homarr-labs/homarr/commit/554d19a458e856141f90e795ecebabf205e945e9))
|
|
||||||
* **deps:** update dependency @paralleldrive/cuid2 to v3 ([#4343](https://github.com/homarr-labs/homarr/issues/4343)) ([074d27e](https://github.com/homarr-labs/homarr/commit/074d27e37e3430e7e98cf28e2876afe009811027))
|
|
||||||
* **deps:** update dependency drizzle-orm to ^0.44.7 ([#4372](https://github.com/homarr-labs/homarr/issues/4372)) ([8242bfc](https://github.com/homarr-labs/homarr/commit/8242bfc95cd0e67397b1bfc46d65aec3f568c304))
|
|
||||||
* **deps:** update dependency ioredis to v5.8.2 ([#4357](https://github.com/homarr-labs/homarr/issues/4357)) ([3c4c26e](https://github.com/homarr-labs/homarr/commit/3c4c26ee214a37193aea31b69ce75322862fc534))
|
|
||||||
* **deps:** update dependency mysql2 to v3.15.3 ([#4360](https://github.com/homarr-labs/homarr/issues/4360)) ([6e6c912](https://github.com/homarr-labs/homarr/commit/6e6c9122257934402848d6fdc51e6ccdfc2dcca0))
|
|
||||||
* **deps:** update dependency next-auth to v5.0.0-beta.30 [security] ([#4379](https://github.com/homarr-labs/homarr/issues/4379)) ([c544d6e](https://github.com/homarr-labs/homarr/commit/c544d6ec2e426bc4c82db547d7c4a5dc4d6e2a27))
|
|
||||||
* **deps:** update dependency next-intl to v4.4.0 ([#4365](https://github.com/homarr-labs/homarr/issues/4365)) ([ce63388](https://github.com/homarr-labs/homarr/commit/ce6338840c20fc46048687ed5566ff12fa4b2502))
|
|
||||||
* **deps:** update dependency node-ical to ^0.22.1 ([#4346](https://github.com/homarr-labs/homarr/issues/4346)) ([ef18c58](https://github.com/homarr-labs/homarr/commit/ef18c58f5b09bafd697365a0182ab7bc8719db2b))
|
|
||||||
* **deps:** update dependency superjson to v2.2.3 ([#4364](https://github.com/homarr-labs/homarr/issues/4364)) ([18cc04e](https://github.com/homarr-labs/homarr/commit/18cc04e7f2740226f7bc7c75c0b0cdaec7cda6f8))
|
|
||||||
* **deps:** update dependency typescript-eslint to ^8.46.2 ([#4352](https://github.com/homarr-labs/homarr/issues/4352)) ([f96cbe0](https://github.com/homarr-labs/homarr/commit/f96cbe06525515112fad466cb8a5f33d74517aba))
|
|
||||||
* **deps:** update testcontainers-node monorepo to ^11.7.2 ([#4366](https://github.com/homarr-labs/homarr/issues/4366)) ([7329135](https://github.com/homarr-labs/homarr/commit/7329135fd85906602a16a9bdc16a1ef1cc4607c5))
|
|
||||||
* **deps:** update tiptap monorepo to v2.26.4 ([#4371](https://github.com/homarr-labs/homarr/issues/4371)) ([136ed8e](https://github.com/homarr-labs/homarr/commit/136ed8e8b401b801ba16f2079bdfb5aeebe9f57f))
|
|
||||||
* **deps:** update trpc monorepo to ^11.7.0 ([#4385](https://github.com/homarr-labs/homarr/issues/4385)) ([24def99](https://github.com/homarr-labs/homarr/commit/24def9982709cd6de1765a7235169fe1ceb3cbea))
|
|
||||||
* **media-releases:** support more media type mappings for jellyfin and emby ([#4382](https://github.com/homarr-labs/homarr/issues/4382)) ([6f3b7ec](https://github.com/homarr-labs/homarr/commit/6f3b7ec4915754e183300a915baebc11f264df62))
|
|
||||||
* **releases-widget:** Search multiple docker hub pages for releases ([#4158](https://github.com/homarr-labs/homarr/issues/4158)) ([e0526c5](https://github.com/homarr-labs/homarr/commit/e0526c5ea7ee83adcd22d78fbf63334cb59bcf79))
|
|
||||||
|
|
||||||
## [1.43.0](https://github.com/homarr-labs/homarr/compare/v1.42.1...v1.43.0) (2025-10-24)
|
## [1.43.0](https://github.com/homarr-labs/homarr/compare/v1.42.1...v1.43.0) (2025-10-24)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -1,9 +1 @@
|
|||||||
# ── CODEOWNERS ───────────────────────────────────────
|
|
||||||
* @homarr-labs/maintainers
|
* @homarr-labs/maintainers
|
||||||
|
|
||||||
# Exempt Renovate‑managed files (no owners)
|
|
||||||
package.json @homarr-labs/none
|
|
||||||
package-lock.json @homarr-labs/none
|
|
||||||
pnpm-lock.yaml @homarr-labs/none
|
|
||||||
Dockerfile @homarr-labs/none
|
|
||||||
docker-compose.yml @homarr-labs/none
|
|
||||||
+3
-2
@@ -1,4 +1,4 @@
|
|||||||
FROM node:24.12.0-alpine AS base
|
FROM node:24.10.0-alpine AS base
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
RUN apk add --no-cache libc6-compat
|
RUN apk add --no-cache libc6-compat
|
||||||
@@ -45,6 +45,7 @@ COPY --from=builder /app/apps/nextjs/package.json .
|
|||||||
|
|
||||||
COPY --from=builder /app/apps/tasks/tasks.cjs ./apps/tasks/tasks.cjs
|
COPY --from=builder /app/apps/tasks/tasks.cjs ./apps/tasks/tasks.cjs
|
||||||
COPY --from=builder /app/apps/websocket/wssServer.cjs ./apps/websocket/wssServer.cjs
|
COPY --from=builder /app/apps/websocket/wssServer.cjs ./apps/websocket/wssServer.cjs
|
||||||
|
COPY --from=builder /app/node_modules/better-sqlite3/build/Release/better_sqlite3.node /app/build/better_sqlite3.node
|
||||||
|
|
||||||
COPY --from=builder /app/packages/db/migrations ./db/migrations
|
COPY --from=builder /app/packages/db/migrations ./db/migrations
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ COPY --from=builder /app/apps/nextjs/.next/standalone ./
|
|||||||
COPY --from=builder /app/apps/nextjs/.next/static ./apps/nextjs/.next/static
|
COPY --from=builder /app/apps/nextjs/.next/static ./apps/nextjs/.next/static
|
||||||
COPY --from=builder /app/apps/nextjs/public ./apps/nextjs/public
|
COPY --from=builder /app/apps/nextjs/public ./apps/nextjs/public
|
||||||
COPY scripts/run.sh ./run.sh
|
COPY scripts/run.sh ./run.sh
|
||||||
COPY --chmod=755 scripts/entrypoint.sh ./entrypoint.sh
|
COPY --chmod=777 scripts/entrypoint.sh ./entrypoint.sh
|
||||||
COPY packages/redis/redis.conf /app/redis.conf
|
COPY packages/redis/redis.conf /app/redis.conf
|
||||||
COPY nginx.conf /etc/nginx/templates/nginx.conf
|
COPY nginx.conf /etc/nginx/templates/nginx.conf
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Importing env files here to validate on build
|
// Importing env files here to validate on build
|
||||||
import "@homarr/auth/env";
|
import "@homarr/auth/env";
|
||||||
import "@homarr/core/infrastructure/db/env";
|
import "@homarr/db/env";
|
||||||
import "@homarr/common/env";
|
import "@homarr/common/env";
|
||||||
import "@homarr/core/infrastructure/logs/env";
|
import "@homarr/log/env";
|
||||||
import "@homarr/docker/env";
|
import "@homarr/docker/env";
|
||||||
|
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
@@ -20,27 +20,19 @@ const withNextIntl = createNextIntlPlugin({
|
|||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
// react compiler breaks mantine-react-table, so disabled for now
|
/** We already do linting and typechecking as separate tasks in CI */
|
||||||
//reactCompiler: true,
|
eslint: { ignoreDuringBuilds: true },
|
||||||
/** We already do typechecking as separate tasks in CI */
|
|
||||||
typescript: { ignoreBuildErrors: true },
|
typescript: { ignoreBuildErrors: true },
|
||||||
/**
|
/**
|
||||||
* dockerode is required in the external server packages because of https://github.com/homarr-labs/homarr/issues/612
|
* dockerode is required in the external server packages because of https://github.com/homarr-labs/homarr/issues/612
|
||||||
* isomorphic-dompurify and jsdom are required, see https://github.com/kkomelin/isomorphic-dompurify/issues/356
|
|
||||||
*/
|
*/
|
||||||
serverExternalPackages: ["dockerode", "isomorphic-dompurify", "jsdom"],
|
serverExternalPackages: ["dockerode"],
|
||||||
experimental: {
|
experimental: {
|
||||||
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
|
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
|
||||||
turbopackFileSystemCacheForDev: true,
|
|
||||||
},
|
},
|
||||||
transpilePackages: ["@homarr/ui", "@homarr/notifications", "@homarr/modals", "@homarr/spotlight", "@homarr/widgets"],
|
transpilePackages: ["@homarr/ui", "@homarr/notifications", "@homarr/modals", "@homarr/spotlight", "@homarr/widgets"],
|
||||||
images: {
|
images: {
|
||||||
localPatterns: [
|
domains: ["cdn.jsdelivr.net"],
|
||||||
{
|
|
||||||
pathname: "/**",
|
|
||||||
search: "",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/require-await,no-restricted-syntax
|
// eslint-disable-next-line @typescript-eslint/require-await,no-restricted-syntax
|
||||||
async headers() {
|
async headers() {
|
||||||
|
|||||||
+32
-32
@@ -23,6 +23,7 @@
|
|||||||
"@homarr/api": "workspace:^0.1.0",
|
"@homarr/api": "workspace:^0.1.0",
|
||||||
"@homarr/auth": "workspace:^0.1.0",
|
"@homarr/auth": "workspace:^0.1.0",
|
||||||
"@homarr/boards": "workspace:^0.1.0",
|
"@homarr/boards": "workspace:^0.1.0",
|
||||||
|
"@homarr/certificates": "workspace:^0.1.0",
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/core": "workspace:^0.1.0",
|
"@homarr/core": "workspace:^0.1.0",
|
||||||
"@homarr/cron-job-status": "workspace:^0.1.0",
|
"@homarr/cron-job-status": "workspace:^0.1.0",
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
"@homarr/icons": "workspace:^0.1.0",
|
"@homarr/icons": "workspace:^0.1.0",
|
||||||
"@homarr/image-proxy": "workspace:^0.1.0",
|
"@homarr/image-proxy": "workspace:^0.1.0",
|
||||||
"@homarr/integrations": "workspace:^0.1.0",
|
"@homarr/integrations": "workspace:^0.1.0",
|
||||||
|
"@homarr/log": "workspace:^",
|
||||||
"@homarr/modals": "workspace:^0.1.0",
|
"@homarr/modals": "workspace:^0.1.0",
|
||||||
"@homarr/modals-collection": "workspace:^0.1.0",
|
"@homarr/modals-collection": "workspace:^0.1.0",
|
||||||
"@homarr/notifications": "workspace:^0.1.0",
|
"@homarr/notifications": "workspace:^0.1.0",
|
||||||
@@ -48,61 +50,59 @@
|
|||||||
"@homarr/ui": "workspace:^0.1.0",
|
"@homarr/ui": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@homarr/widgets": "workspace:^0.1.0",
|
"@homarr/widgets": "workspace:^0.1.0",
|
||||||
"@mantine/colors-generator": "^8.3.10",
|
"@mantine/colors-generator": "^8.3.5",
|
||||||
"@mantine/core": "^8.3.10",
|
"@mantine/core": "^8.3.5",
|
||||||
"@mantine/dropzone": "^8.3.10",
|
"@mantine/dropzone": "^8.3.5",
|
||||||
"@mantine/hooks": "^8.3.10",
|
"@mantine/hooks": "^8.3.5",
|
||||||
"@mantine/modals": "^8.3.10",
|
"@mantine/modals": "^8.3.5",
|
||||||
"@mantine/tiptap": "^8.3.10",
|
"@mantine/tiptap": "^8.3.5",
|
||||||
"@million/lint": "1.0.14",
|
"@million/lint": "1.0.14",
|
||||||
"@tabler/icons-react": "^3.35.0",
|
"@tabler/icons-react": "^3.35.0",
|
||||||
"@tanstack/react-query": "^5.90.12",
|
"@tanstack/react-query": "^5.90.5",
|
||||||
"@tanstack/react-query-devtools": "^5.91.1",
|
"@tanstack/react-query-devtools": "^5.90.2",
|
||||||
"@tanstack/react-query-next-experimental": "^5.91.0",
|
"@tanstack/react-query-next-experimental": "^5.90.2",
|
||||||
"@trpc/client": "^11.8.0",
|
"@trpc/client": "^11.6.0",
|
||||||
"@trpc/next": "^11.8.0",
|
"@trpc/next": "^11.6.0",
|
||||||
"@trpc/react-query": "^11.8.0",
|
"@trpc/react-query": "^11.6.0",
|
||||||
"@trpc/server": "^11.8.0",
|
"@trpc/server": "^11.6.0",
|
||||||
"@xterm/addon-canvas": "^0.7.0",
|
"@xterm/addon-canvas": "^0.7.0",
|
||||||
"@xterm/addon-fit": "0.10.0",
|
"@xterm/addon-fit": "0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"babel-plugin-react-compiler": "^1.0.0",
|
"chroma-js": "^3.1.2",
|
||||||
"chroma-js": "^3.2.0",
|
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.18",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"flag-icons": "^7.5.0",
|
"flag-icons": "^7.5.0",
|
||||||
"glob": "^13.0.0",
|
"glob": "^11.0.3",
|
||||||
"isomorphic-dompurify": "^2.34.0",
|
"jotai": "^2.15.0",
|
||||||
"jotai": "^2.16.0",
|
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.0.10",
|
"next": "15.5.6",
|
||||||
"postcss-preset-mantine": "^1.18.0",
|
"postcss-preset-mantine": "^1.18.0",
|
||||||
"prismjs": "^1.30.0",
|
"prismjs": "^1.30.0",
|
||||||
"react": "19.2.3",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.0",
|
||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-simple-code-editor": "^0.14.1",
|
"react-simple-code-editor": "^0.14.1",
|
||||||
"sass": "^1.96.0",
|
"sass": "^1.93.2",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.3",
|
||||||
"swagger-ui-react": "^5.31.0",
|
"swagger-ui-react": "^5.29.5",
|
||||||
"use-deep-compare-effect": "^1.8.1",
|
"use-deep-compare-effect": "^1.8.1",
|
||||||
"zod": "^4.1.13"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/chroma-js": "3.1.2",
|
"@types/chroma-js": "3.1.1",
|
||||||
"@types/node": "^24.10.4",
|
"@types/node": "^24.9.1",
|
||||||
"@types/prismjs": "^1.26.5",
|
"@types/prismjs": "^1.26.5",
|
||||||
"@types/react": "19.2.7",
|
"@types/react": "19.2.2",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.2",
|
||||||
"@types/swagger-ui-react": "^5.18.0",
|
"@types/swagger-ui-react": "^5.18.0",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.38.0",
|
||||||
"node-loader": "^2.1.0",
|
"node-loader": "^2.1.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,23 +64,16 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
|
|||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<TextInput
|
<TextInput label={t("field.username.label")} autoComplete="off" {...form.getInputProps("username")} />
|
||||||
label={t("field.username.label")}
|
|
||||||
id="username"
|
|
||||||
autoComplete="username"
|
|
||||||
{...form.getInputProps("username")}
|
|
||||||
/>
|
|
||||||
<CustomPasswordInput
|
<CustomPasswordInput
|
||||||
withPasswordRequirements
|
withPasswordRequirements
|
||||||
label={t("field.password.label")}
|
label={t("field.password.label")}
|
||||||
id="password"
|
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
{...form.getInputProps("password")}
|
{...form.getInputProps("password")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label={t("field.passwordConfirm.label")}
|
label={t("field.passwordConfirm.label")}
|
||||||
id="password-confirm"
|
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
{...form.getInputProps("confirmPassword")}
|
{...form.getInputProps("confirmPassword")}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -116,18 +116,8 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
|
|||||||
<>
|
<>
|
||||||
<form onSubmit={form.onSubmit((credentials) => void signInAsync(credentials.provider, credentials))}>
|
<form onSubmit={form.onSubmit((credentials) => void signInAsync(credentials.provider, credentials))}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<TextInput
|
<TextInput label={t("field.username.label")} {...form.getInputProps("name")} />
|
||||||
label={t("field.username.label")}
|
<PasswordInput label={t("field.password.label")} {...form.getInputProps("password")} />
|
||||||
id="username"
|
|
||||||
autoComplete="username"
|
|
||||||
{...form.getInputProps("name")}
|
|
||||||
/>
|
|
||||||
<PasswordInput
|
|
||||||
label={t("field.password.label")}
|
|
||||||
id="password"
|
|
||||||
autoComplete="current-password"
|
|
||||||
{...form.getInputProps("password")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{providers.includes("credentials") && (
|
{providers.includes("credentials") && (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ import { IntegrationProvider } from "@homarr/auth/client";
|
|||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server";
|
import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server";
|
||||||
import { isNullOrWhitespace } from "@homarr/common";
|
import { isNullOrWhitespace } from "@homarr/common";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
|
||||||
import type { WidgetKind } from "@homarr/definitions";
|
import type { WidgetKind } from "@homarr/definitions";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
import { getI18n } from "@homarr/translation/server";
|
import { getI18n } from "@homarr/translation/server";
|
||||||
import { prefetchForKindAsync } from "@homarr/widgets/prefetch";
|
import { prefetchForKindAsync } from "@homarr/widgets/prefetch";
|
||||||
|
|
||||||
@@ -23,8 +22,6 @@ import type { Board, Item } from "../_types";
|
|||||||
import { DynamicClientBoard } from "./_dynamic-client";
|
import { DynamicClientBoard } from "./_dynamic-client";
|
||||||
import { BoardContentHeaderActions } from "./_header-actions";
|
import { BoardContentHeaderActions } from "./_header-actions";
|
||||||
|
|
||||||
const logger = createLogger({ module: "createBoardContentPage" });
|
|
||||||
|
|
||||||
export type Params = Record<string, unknown>;
|
export type Params = Record<string, unknown>;
|
||||||
|
|
||||||
interface Props<TParams extends Params> {
|
interface Props<TParams extends Params> {
|
||||||
@@ -60,13 +57,7 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
|
|||||||
|
|
||||||
for (const [kind, items] of itemsMap) {
|
for (const [kind, items] of itemsMap) {
|
||||||
await prefetchForKindAsync(kind, queryClient, items).catch((error) => {
|
await prefetchForKindAsync(kind, queryClient, items).catch((error) => {
|
||||||
logger.error(
|
logger.error(new Error("Failed to prefetch widget", { cause: error }));
|
||||||
new ErrorWithMetadata(
|
|
||||||
"Failed to prefetch widget",
|
|
||||||
{ widgetKind: kind, itemCount: items.length },
|
|
||||||
{ cause: error },
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useCallback, useEffect } from "react";
|
import { useCallback, useEffect } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Group, Menu, ScrollArea } from "@mantine/core";
|
import { Group, Menu, ScrollArea } from "@mantine/core";
|
||||||
import { useHotkeys } from "@mantine/hooks";
|
import { useHotkeys } from "@mantine/hooks";
|
||||||
@@ -28,7 +29,6 @@ import { useConfirmModal, useModalAction } from "@homarr/modals";
|
|||||||
import { AppSelectModal } from "@homarr/modals-collection";
|
import { AppSelectModal } from "@homarr/modals-collection";
|
||||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { useItemActions } from "~/components/board/items/item-actions";
|
import { useItemActions } from "~/components/board/items/item-actions";
|
||||||
import { ItemSelectModal } from "~/components/board/items/item-select-modal";
|
import { ItemSelectModal } from "~/components/board/items/item-select-modal";
|
||||||
@@ -44,7 +44,7 @@ export const BoardContentHeaderActions = () => {
|
|||||||
const { hasChangeAccess } = useBoardPermissions(board);
|
const { hasChangeAccess } = useBoardPermissions(board);
|
||||||
|
|
||||||
if (!hasChangeAccess) {
|
if (!hasChangeAccess) {
|
||||||
return <SelectBoardsMenu />;
|
return null; // Hide actions for user without access
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { TRPCError } from "@trpc/server";
|
|||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { BoardProvider } from "@homarr/boards/context";
|
import { BoardProvider } from "@homarr/boards/context";
|
||||||
import { EditModeProvider } from "@homarr/boards/edit-mode";
|
import { EditModeProvider } from "@homarr/boards/edit-mode";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { logger } from "@homarr/log";
|
||||||
|
|
||||||
import { MainHeader } from "~/components/layout/header";
|
import { MainHeader } from "~/components/layout/header";
|
||||||
import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo";
|
import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo";
|
||||||
@@ -18,8 +18,6 @@ import { CustomCss } from "./(content)/_custom-css";
|
|||||||
import { BoardReadyProvider } from "./(content)/_ready-context";
|
import { BoardReadyProvider } from "./(content)/_ready-context";
|
||||||
import { BoardMantineProvider } from "./(content)/_theme";
|
import { BoardMantineProvider } from "./(content)/_theme";
|
||||||
|
|
||||||
const logger = createLogger({ module: "createBoardLayout" });
|
|
||||||
|
|
||||||
interface CreateBoardLayoutProps<TParams extends Params> {
|
interface CreateBoardLayoutProps<TParams extends Params> {
|
||||||
headerActions: JSX.Element;
|
headerActions: JSX.Element;
|
||||||
getInitialBoardAsync: (params: TParams) => Promise<Board>;
|
getInitialBoardAsync: (params: TParams) => Promise<Board>;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import type { MantineColor } from "@mantine/core";
|
import type { MantineColor } from "@mantine/core";
|
||||||
import { Button, Card, Stack, Text } from "@mantine/core";
|
import { Button, Card, Stack, Text } from "@mantine/core";
|
||||||
import { IconBook2, IconCategoryPlus, IconLayoutDashboard, IconMailForward } from "@tabler/icons-react";
|
import { IconBook2, IconCategoryPlus, IconLayoutDashboard, IconMailForward } from "@tabler/icons-react";
|
||||||
@@ -7,7 +8,6 @@ import { getMantineColor } from "@homarr/common";
|
|||||||
import { db } from "@homarr/db";
|
import { db } from "@homarr/db";
|
||||||
import { createDocumentationLink } from "@homarr/definitions";
|
import { createDocumentationLink } from "@homarr/definitions";
|
||||||
import { getScopedI18n } from "@homarr/translation/server";
|
import { getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
import type { TablerIcon } from "@homarr/ui";
|
import type { TablerIcon } from "@homarr/ui";
|
||||||
|
|
||||||
export const InitFinish = async () => {
|
export const InitFinish = async () => {
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ import { groupCreateSchema } from "@homarr/validation/group";
|
|||||||
|
|
||||||
export const InitGroup = () => {
|
export const InitGroup = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { mutateAsync } = clientApi.group.createInitialExternalGroup.useMutation({
|
const { mutateAsync } = clientApi.group.createInitialExternalGroup.useMutation();
|
||||||
async onSuccess() {
|
|
||||||
await revalidatePathActionAsync("/init");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const form = useZodForm(groupCreateSchema, {
|
const form = useZodForm(groupCreateSchema, {
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name: "",
|
name: "",
|
||||||
@@ -25,6 +21,9 @@ export const InitGroup = () => {
|
|||||||
|
|
||||||
const handleSubmitAsync = async (values: z.infer<typeof groupCreateSchema>) => {
|
const handleSubmitAsync = async (values: z.infer<typeof groupCreateSchema>) => {
|
||||||
await mutateAsync(values, {
|
await mutateAsync(values, {
|
||||||
|
async onSuccess() {
|
||||||
|
await revalidatePathActionAsync("/init");
|
||||||
|
},
|
||||||
onError(error) {
|
onError(error) {
|
||||||
if (error.data?.code === "CONFLICT") {
|
if (error.data?.code === "CONFLICT") {
|
||||||
form.setErrors({ name: t("common.zod.errors.custom.groupNameTaken") });
|
form.setErrors({ name: t("common.zod.errors.custom.groupNameTaken") });
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ import { settingsInitSchema } from "@homarr/validation/settings";
|
|||||||
export const InitSettings = () => {
|
export const InitSettings = () => {
|
||||||
const tSection = useScopedI18n("management.page.settings.section");
|
const tSection = useScopedI18n("management.page.settings.section");
|
||||||
const t = useI18n();
|
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 });
|
const form = useZodForm(settingsInitSchema, { initialValues: defaultServerSettings });
|
||||||
|
|
||||||
form.watch("analytics.enableGeneral", ({ value }) => {
|
form.watch("analytics.enableGeneral", ({ value }) => {
|
||||||
@@ -35,7 +31,11 @@ export const InitSettings = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmitAsync = async (values: z.infer<typeof settingsInitSchema>) => {
|
const handleSubmitAsync = async (values: z.infer<typeof settingsInitSchema>) => {
|
||||||
await mutateAsync(values);
|
await mutateAsync(values, {
|
||||||
|
async onSuccess() {
|
||||||
|
await revalidatePathActionAsync("/init");
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -26,19 +26,19 @@ export const InitUserForm = () => {
|
|||||||
|
|
||||||
const handleSubmitAsync = async (values: FormType) => {
|
const handleSubmitAsync = async (values: FormType) => {
|
||||||
await mutateAsync(values, {
|
await mutateAsync(values, {
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
title: tUser("notification.success.title"),
|
title: tUser("notification.success.title"),
|
||||||
message: tUser("notification.success.message"),
|
message: tUser("notification.success.message"),
|
||||||
});
|
});
|
||||||
|
|
||||||
void signIn("credentials", {
|
await signIn("credentials", {
|
||||||
name: values.username,
|
name: values.username,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
redirect: false,
|
redirect: false,
|
||||||
}).then(async () => {
|
|
||||||
await revalidatePathActionAsync("/init");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await revalidatePathActionAsync("/init");
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
showErrorNotification({
|
showErrorNotification({
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
|
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
import { IconBox, IconPencil } from "@tabler/icons-react";
|
import { IconBox, IconPencil } from "@tabler/icons-react";
|
||||||
@@ -9,7 +10,7 @@ import { api } from "@homarr/api/server";
|
|||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
||||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link, SearchInput, TablePagination } from "@homarr/ui";
|
import { SearchInput, TablePagination } from "@homarr/ui";
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Menu } from "@mantine/core";
|
import { Menu } from "@mantine/core";
|
||||||
import { IconCopy, IconDeviceMobile, IconHome, IconSettings, IconTrash } from "@tabler/icons-react";
|
import { IconCopy, IconDeviceMobile, IconHome, IconSettings, IconTrash } from "@tabler/icons-react";
|
||||||
|
|
||||||
@@ -11,7 +12,6 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
|
|||||||
import { useConfirmModal, useModalAction } from "@homarr/modals";
|
import { useConfirmModal, useModalAction } from "@homarr/modals";
|
||||||
import { DuplicateBoardModal } from "@homarr/modals-collection";
|
import { DuplicateBoardModal } from "@homarr/modals-collection";
|
||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { useBoardPermissions } from "~/components/board/permissions/client";
|
import { useBoardPermissions } from "~/components/board/permissions/client";
|
||||||
|
|
||||||
@@ -84,6 +84,9 @@ export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) =>
|
|||||||
id: board.id,
|
id: board.id,
|
||||||
name: board.name,
|
name: board.name,
|
||||||
},
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
await revalidatePathActionAsync("/manage/boards");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}, [board.id, board.name, openDuplicateModal]);
|
}, [board.id, board.name, openDuplicateModal]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -20,7 +21,7 @@ import type { RouterOutputs } from "@homarr/api";
|
|||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { getScopedI18n } from "@homarr/translation/server";
|
import { getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link, UserAvatar } from "@homarr/ui";
|
import { UserAvatar } from "@homarr/ui";
|
||||||
|
|
||||||
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
|
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Alert, Anchor, Button, ButtonGroup, Fieldset, Group, Stack, Text, TextInput } from "@mantine/core";
|
import { Alert, Anchor, Button, ButtonGroup, Fieldset, Group, Stack, Text, TextInput } from "@mantine/core";
|
||||||
import { IconInfoCircle, IconPencil, IconPlus, IconUnlink } from "@tabler/icons-react";
|
import { IconInfoCircle, IconPencil, IconPlus, IconUnlink } from "@tabler/icons-react";
|
||||||
@@ -16,7 +17,6 @@ import { useConfirmModal, useModalAction } from "@homarr/modals";
|
|||||||
import { AppSelectModal } from "@homarr/modals-collection";
|
import { AppSelectModal } from "@homarr/modals-collection";
|
||||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
import { integrationUpdateSchema } from "@homarr/validation/integration";
|
import { integrationUpdateSchema } from "@homarr/validation/integration";
|
||||||
|
|
||||||
import { SecretCard } from "../../_components/secrets/integration-secret-card";
|
import { SecretCard } from "../../_components/secrets/integration-secret-card";
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
import type { ChangeEvent } from "react";
|
import type { ChangeEvent } from "react";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Flex, Group, Menu, ScrollArea, Text, TextInput } from "@mantine/core";
|
import { Flex, Group, Menu, ScrollArea, Text, TextInput } from "@mantine/core";
|
||||||
import { IconSearch } from "@tabler/icons-react";
|
import { IconSearch } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { getIntegrationName, integrationKinds } from "@homarr/definitions";
|
import { getIntegrationName, integrationKinds } from "@homarr/definitions";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { IntegrationAvatar, Link } from "@homarr/ui";
|
import { IntegrationAvatar } from "@homarr/ui";
|
||||||
|
|
||||||
interface IntegrationCreateDropdownContentProps {
|
interface IntegrationCreateDropdownContentProps {
|
||||||
enableMockIntegration: boolean;
|
enableMockIntegration: boolean;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { startTransition, useState } from "react";
|
import { startTransition, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
@@ -35,7 +36,6 @@ import type { GetInputPropsReturnType, UseFormReturnType } from "@homarr/form";
|
|||||||
import { useZodForm } from "@homarr/form";
|
import { useZodForm } from "@homarr/form";
|
||||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
import { appHrefSchema } from "@homarr/validation/app";
|
import { appHrefSchema } from "@homarr/validation/app";
|
||||||
import { integrationCreateSchema } from "@homarr/validation/integration";
|
import { integrationCreateSchema } from "@homarr/validation/integration";
|
||||||
|
|
||||||
@@ -86,11 +86,7 @@ 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 [error, setError] = useState<null | AnyMappedTestConnectionError>(null);
|
||||||
|
|
||||||
const handleSubmitAsync = async ({ appId, appHref, hasApp, ...values }: FormType) => {
|
const handleSubmitAsync = async ({ appId, appHref, hasApp, ...values }: FormType) => {
|
||||||
@@ -120,7 +116,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
|
|||||||
app,
|
app,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess(data) {
|
async onSuccess(data) {
|
||||||
// We do it this way as we are unable to send a typesafe error through onError
|
// We do it this way as we are unable to send a typesafe error through onError
|
||||||
if (data?.error) {
|
if (data?.error) {
|
||||||
setError(data.error);
|
setError(data.error);
|
||||||
@@ -136,7 +132,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
|
|||||||
message: t("integration.page.create.notification.success.message"),
|
message: t("integration.page.create.notification.success.message"),
|
||||||
});
|
});
|
||||||
|
|
||||||
router.push("/manage/integrations");
|
await revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations"));
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
showErrorNotification({
|
showErrorNotification({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import type { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
AccordionControl,
|
AccordionControl,
|
||||||
@@ -35,7 +36,7 @@ import { objectEntries } from "@homarr/common";
|
|||||||
import type { IntegrationKind } from "@homarr/definitions";
|
import type { IntegrationKind } from "@homarr/definitions";
|
||||||
import { getIntegrationName } from "@homarr/definitions";
|
import { getIntegrationName } from "@homarr/definitions";
|
||||||
import { getScopedI18n } from "@homarr/translation/server";
|
import { getScopedI18n } from "@homarr/translation/server";
|
||||||
import { CountBadge, IntegrationAvatar, Link } from "@homarr/ui";
|
import { CountBadge, IntegrationAvatar } from "@homarr/ui";
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
@@ -24,7 +25,7 @@ import { humanFileSize } from "@homarr/common";
|
|||||||
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
||||||
import { createLocalImageUrl } from "@homarr/icons/local";
|
import { createLocalImageUrl } from "@homarr/icons/local";
|
||||||
import { getI18n } from "@homarr/translation/server";
|
import { getI18n } from "@homarr/translation/server";
|
||||||
import { Link, SearchInput, TablePagination, UserAvatar } from "@homarr/ui";
|
import { SearchInput, TablePagination, UserAvatar } from "@homarr/ui";
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
import Link from "next/link";
|
||||||
import { Card, Group, SimpleGrid, Space, Stack, Text } from "@mantine/core";
|
import { Card, Group, SimpleGrid, Space, Stack, Text } from "@mantine/core";
|
||||||
import { IconArrowRight } from "@tabler/icons-react";
|
import { IconArrowRight } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
import { getScopedI18n } from "@homarr/translation/server";
|
import { getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
import { createMetaTitle } from "~/metadata";
|
import { createMetaTitle } from "~/metadata";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import type { SegmentedControlItem } from "@mantine/core";
|
import type { SegmentedControlItem } from "@mantine/core";
|
||||||
import { Button, Fieldset, Grid, Group, SegmentedControl, Stack, Textarea, TextInput } 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 { WidgetIntegrationSelect } from "node_modules/@homarr/widgets/src/widget-integration-select";
|
||||||
@@ -11,7 +12,6 @@ import { useZodForm } from "@homarr/form";
|
|||||||
import { IconPicker } from "@homarr/forms-collection";
|
import { IconPicker } from "@homarr/forms-collection";
|
||||||
import type { TranslationFunction } from "@homarr/translation";
|
import type { TranslationFunction } from "@homarr/translation";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
import { searchEngineManageSchema } from "@homarr/validation/search-engine";
|
import { searchEngineManageSchema } from "@homarr/validation/search-engine";
|
||||||
|
|
||||||
type FormType = z.infer<typeof searchEngineManageSchema>;
|
type FormType = z.infer<typeof searchEngineManageSchema>;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
|
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
import { IconPencil, IconSearch } from "@tabler/icons-react";
|
import { IconPencil, IconSearch } from "@tabler/icons-react";
|
||||||
@@ -8,7 +9,7 @@ import { api } from "@homarr/api/server";
|
|||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
||||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link, SearchInput, TablePagination } from "@homarr/ui";
|
import { SearchInput, TablePagination } from "@homarr/ui";
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
||||||
|
|||||||
+3
-6
@@ -15,11 +15,7 @@ interface RemoveCertificateProps {
|
|||||||
|
|
||||||
export const RemoveCertificate = ({ fileName }: RemoveCertificateProps) => {
|
export const RemoveCertificate = ({ fileName }: RemoveCertificateProps) => {
|
||||||
const { openConfirmModal } = useConfirmModal();
|
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 t = useI18n();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
@@ -31,11 +27,12 @@ export const RemoveCertificate = ({ fileName }: RemoveCertificateProps) => {
|
|||||||
await mutateAsync(
|
await mutateAsync(
|
||||||
{ fileName },
|
{ fileName },
|
||||||
{
|
{
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
title: t("certificate.action.remove.notification.success.title"),
|
title: t("certificate.action.remove.notification.success.title"),
|
||||||
message: t("certificate.action.remove.notification.success.message"),
|
message: t("certificate.action.remove.notification.success.message"),
|
||||||
});
|
});
|
||||||
|
await revalidatePathActionAsync("/manage/tools/certificates");
|
||||||
},
|
},
|
||||||
onError() {
|
onError() {
|
||||||
showErrorNotification({
|
showErrorNotification({
|
||||||
|
|||||||
+3
-6
@@ -15,11 +15,7 @@ interface RemoveHostnameActionIconProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const RemoveHostnameActionIcon = (input: 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 { openConfirmModal } = useConfirmModal();
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
@@ -30,7 +26,8 @@ export const RemoveHostnameActionIcon = (input: RemoveHostnameActionIconProps) =
|
|||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
await mutateAsync(input, {
|
await mutateAsync(input, {
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
|
await revalidatePathActionAsync("/manage/tools/certificates/hostnames");
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
title: t("certificate.action.removeHostname.notification.success.title"),
|
title: t("certificate.action.removeHostname.notification.success.title"),
|
||||||
message: t("certificate.action.removeHostname.notification.success.message"),
|
message: t("certificate.action.removeHostname.notification.success.message"),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { X509Certificate } from "node:crypto";
|
import { X509Certificate } from "node:crypto";
|
||||||
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -16,9 +17,8 @@ import {
|
|||||||
import { IconCertificateOff } from "@tabler/icons-react";
|
import { IconCertificateOff } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { getTrustedCertificateHostnamesAsync } from "@homarr/core/infrastructure/certificates";
|
import { getTrustedCertificateHostnamesAsync } from "@homarr/certificates/server";
|
||||||
import { getI18n } from "@homarr/translation/server";
|
import { getI18n } from "@homarr/translation/server";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
import { NoResults } from "~/components/no-results";
|
import { NoResults } from "~/components/no-results";
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { X509Certificate } from "node:crypto";
|
import { X509Certificate } from "node:crypto";
|
||||||
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { Button, Card, Group, SimpleGrid, Stack, Text, Title } from "@mantine/core";
|
import { Button, Card, Group, SimpleGrid, Stack, Text, Title } from "@mantine/core";
|
||||||
import { IconAlertTriangle, IconCertificate, IconCertificateOff } from "@tabler/icons-react";
|
import { IconAlertTriangle, IconCertificate, IconCertificateOff } from "@tabler/icons-react";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
|
import { loadCustomRootCertificatesAsync } from "@homarr/certificates/server";
|
||||||
import { getMantineColor } from "@homarr/common";
|
import { getMantineColor } from "@homarr/common";
|
||||||
import { loadCustomRootCertificatesAsync } from "@homarr/core/infrastructure/certificates";
|
|
||||||
import type { SupportedLanguage } from "@homarr/translation";
|
import type { SupportedLanguage } from "@homarr/translation";
|
||||||
import { getI18n } from "@homarr/translation/server";
|
import { getI18n } from "@homarr/translation/server";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
import { NoResults } from "~/components/no-results";
|
import { NoResults } from "~/components/no-results";
|
||||||
|
|||||||
@@ -206,16 +206,15 @@ const ContainerActionBarButton = (props: ContainerActionBarButtonProps) => {
|
|||||||
const t = useScopedI18n("docker.action");
|
const t = useScopedI18n("docker.action");
|
||||||
const utils = clientApi.useUtils();
|
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 () => {
|
const handleClickAsync = async () => {
|
||||||
await mutateAsync(
|
await mutateAsync(
|
||||||
{ ids: props.selectedIds },
|
{ ids: props.selectedIds },
|
||||||
{
|
{
|
||||||
|
async onSettled() {
|
||||||
|
await utils.docker.getContainers.invalidate();
|
||||||
|
},
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
title: t(`${props.action}.notification.success.title`),
|
title: t(`${props.action}.notification.success.title`),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { Anchor, Center, Stack, Text } from "@mantine/core";
|
import { Anchor, Center, Stack, Text } from "@mantine/core";
|
||||||
import { IconShipOff } from "@tabler/icons-react";
|
import { IconShipOff } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
export default function DockerErrorPage() {
|
export default function DockerErrorPage() {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { Anchor, Center, Stack, Text } from "@mantine/core";
|
import { Anchor, Center, Stack, Text } from "@mantine/core";
|
||||||
import { IconCubeOff } from "@tabler/icons-react";
|
import { IconCubeOff } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
export default function KubernetesErrorPage() {
|
export default function KubernetesErrorPage() {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|||||||
+1
-1
@@ -1,11 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
import { Card, Group, Text } from "@mantine/core";
|
import { Card, Group, Text } from "@mantine/core";
|
||||||
import { IconArrowRight } from "@tabler/icons-react";
|
import { IconArrowRight } from "@tabler/icons-react";
|
||||||
|
|
||||||
import type { KubernetesLabelResourceType } from "@homarr/definitions";
|
import type { KubernetesLabelResourceType } from "@homarr/definitions";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import classes from "./resource-tile.module.css";
|
import classes from "./resource-tile.module.css";
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { Select } from "@mantine/core";
|
import { Select } from "@mantine/core";
|
||||||
|
|
||||||
import type { LogLevel } from "@homarr/core/infrastructure/logs/constants";
|
import type { LogLevel } from "@homarr/log/constants";
|
||||||
import { logLevelConfiguration, logLevels } from "@homarr/core/infrastructure/logs/constants";
|
import { logLevelConfiguration, logLevels } from "@homarr/log/constants";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import { useLogContext } from "./log-context";
|
import { useLogContext } from "./log-context";
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import type { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
import { createContext, useContext, useMemo, useState } from "react";
|
import { createContext, useContext, useMemo, useState } from "react";
|
||||||
|
|
||||||
import type { LogLevel } from "@homarr/core/infrastructure/logs/constants";
|
import type { LogLevel } from "@homarr/log/constants";
|
||||||
import { logLevels } from "@homarr/core/infrastructure/logs/constants";
|
import { logLevels } from "@homarr/log/constants";
|
||||||
|
|
||||||
const LogContext = createContext<{
|
const LogContext = createContext<{
|
||||||
level: LogLevel;
|
level: LogLevel;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import "@xterm/xterm/css/xterm.css";
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { logsEnv } from "@homarr/core/infrastructure/logs/env";
|
import { env } from "@homarr/log/env";
|
||||||
|
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
|
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
|
||||||
@@ -35,7 +35,7 @@ export default async function LogsManagementPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LogContextProvider defaultLevel={logsEnv.LEVEL}>
|
<LogContextProvider defaultLevel={env.LOG_LEVEL}>
|
||||||
<Group justify="space-between" align="center" wrap="nowrap">
|
<Group justify="space-between" align="center" wrap="nowrap">
|
||||||
<DynamicBreadcrumb />
|
<DynamicBreadcrumb />
|
||||||
<LogLevelSelection />
|
<LogLevelSelection />
|
||||||
|
|||||||
+7
-8
@@ -18,12 +18,7 @@ interface UserProfileAvatarForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const UserProfileAvatarForm = ({ user }: 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 [opened, { toggle }] = useDisclosure(false);
|
||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -43,7 +38,9 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
|
|||||||
image: base64Url,
|
image: base64Url,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
|
// Revalidate all as the avatar is used in multiple places
|
||||||
|
await revalidatePathActionAsync("/");
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
message: tManageAvatar("changeImage.notification.success.message"),
|
message: tManageAvatar("changeImage.notification.success.message"),
|
||||||
});
|
});
|
||||||
@@ -77,7 +74,9 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
|
|||||||
image: null,
|
image: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
|
// Revalidate all as the avatar is used in multiple places
|
||||||
|
await revalidatePathActionAsync("/");
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
message: tManageAvatar("removeImage.notification.success.message"),
|
message: tManageAvatar("removeImage.notification.success.message"),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Anchor, Button, Group, Text, Title, Tooltip } from "@mantine/core";
|
import { Anchor, Button, Group, Text, Title, Tooltip } from "@mantine/core";
|
||||||
import { IconCheck } from "@tabler/icons-react";
|
import { IconCheck } from "@tabler/icons-react";
|
||||||
import type { MRT_ColumnDef } from "mantine-react-table";
|
import type { MRT_ColumnDef } from "mantine-react-table";
|
||||||
@@ -9,7 +10,7 @@ import { MantineReactTable } from "mantine-react-table";
|
|||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||||
import { Link, UserAvatar } from "@homarr/ui";
|
import { UserAvatar } from "@homarr/ui";
|
||||||
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
|
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
|
||||||
|
|
||||||
interface UserListComponentProps {
|
interface UserListComponentProps {
|
||||||
|
|||||||
+8
-7
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { startTransition, useCallback, useState } from "react";
|
import { startTransition, useCallback, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
@@ -20,9 +20,8 @@ import { IconPlus, IconUserCheck } from "@tabler/icons-react";
|
|||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
import type { GroupPermissionKey } from "@homarr/definitions";
|
|
||||||
import { everyoneGroup, groupPermissions } from "@homarr/definitions";
|
import { everyoneGroup, groupPermissions } from "@homarr/definitions";
|
||||||
import type { IsValid } from "@homarr/form";
|
import type { GroupPermissionKey } from "@homarr/definitions";
|
||||||
import { useZodForm } from "@homarr/form";
|
import { useZodForm } from "@homarr/form";
|
||||||
import { useModalAction } from "@homarr/modals";
|
import { useModalAction } from "@homarr/modals";
|
||||||
import { showErrorNotification } from "@homarr/notifications";
|
import { showErrorNotification } from "@homarr/notifications";
|
||||||
@@ -114,9 +113,11 @@ export const UserCreateStepperComponent = ({ initialGroups }: UserCreateStepperC
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const allForms = [generalForm, securityForm, groupsForm];
|
const allForms = useMemo(() => [generalForm, securityForm, groupsForm], [generalForm, securityForm, groupsForm]);
|
||||||
const isValidCallback: IsValid<unknown> | undefined = allForms[active]?.isValid;
|
|
||||||
const currentFormValid = isValidCallback?.() ?? true;
|
const activeForm = allForms[active];
|
||||||
|
const isCurrentFormValid = activeForm ? activeForm.isValid : () => true;
|
||||||
|
const canNavigateToNextStep = isCurrentFormValid();
|
||||||
|
|
||||||
const controlledGoToNextStep = useCallback(async () => {
|
const controlledGoToNextStep = useCallback(async () => {
|
||||||
if (active + 1 === stepperMax) {
|
if (active + 1 === stepperMax) {
|
||||||
@@ -217,7 +218,7 @@ export const UserCreateStepperComponent = ({ initialGroups }: UserCreateStepperC
|
|||||||
</Stepper.Completed>
|
</Stepper.Completed>
|
||||||
</Stepper>
|
</Stepper>
|
||||||
<StepperNavigationComponent
|
<StepperNavigationComponent
|
||||||
hasNext={hasNext && currentFormValid}
|
hasNext={hasNext && canNavigateToNextStep}
|
||||||
hasPrevious={hasPrevious}
|
hasPrevious={hasPrevious}
|
||||||
isComplete={active === stepperMax}
|
isComplete={active === stepperMax}
|
||||||
isLoadingNextStep={isPending}
|
isLoadingNextStep={isPending}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { Button, Card, Group } from "@mantine/core";
|
import { Button, Card, Group } from "@mantine/core";
|
||||||
import { IconArrowBackUp, IconArrowLeft, IconArrowRight, IconRotate } from "@tabler/icons-react";
|
import { IconArrowBackUp, IconArrowLeft, IconArrowRight, IconRotate } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
interface StepperNavigationComponentProps {
|
interface StepperNavigationComponentProps {
|
||||||
hasPrevious: boolean;
|
hasPrevious: boolean;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { NavLink } from "@mantine/core";
|
import { NavLink } from "@mantine/core";
|
||||||
|
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
interface NavigationLinkProps {
|
interface NavigationLinkProps {
|
||||||
href: string;
|
href: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { Alert, Anchor } from "@mantine/core";
|
import { Alert, Anchor } from "@mantine/core";
|
||||||
import { IconExclamationCircle } from "@tabler/icons-react";
|
import { IconExclamationCircle } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { createDocumentationLink } from "@homarr/definitions";
|
import { createDocumentationLink } from "@homarr/definitions";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
export const ReservedGroupAlert = () => {
|
export const ReservedGroupAlert = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|||||||
@@ -25,11 +25,7 @@ export const TransferGroupOwnership = ({ group }: TransferGroupOwnershipProps) =
|
|||||||
const [innerOwnerId, setInnerOwnerId] = useState(group.ownerId);
|
const [innerOwnerId, setInnerOwnerId] = useState(group.ownerId);
|
||||||
const { openModal } = useModalAction(UserSelectModal);
|
const { openModal } = useModalAction(UserSelectModal);
|
||||||
const { openConfirmModal } = useConfirmModal();
|
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(() => {
|
const handleTransfer = useCallback(() => {
|
||||||
openModal(
|
openModal(
|
||||||
@@ -51,7 +47,7 @@ export const TransferGroupOwnership = ({ group }: TransferGroupOwnershipProps) =
|
|||||||
userId: id,
|
userId: id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
setInnerOwnerId(id);
|
setInnerOwnerId(id);
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
title: tRoot("common.notification.transfer.success"),
|
title: tRoot("common.notification.transfer.success"),
|
||||||
@@ -60,6 +56,7 @@ export const TransferGroupOwnership = ({ group }: TransferGroupOwnershipProps) =
|
|||||||
user: name,
|
user: name,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
await revalidatePathActionAsync(`/manage/users/groups/${group.id}`);
|
||||||
},
|
},
|
||||||
onError() {
|
onError() {
|
||||||
showErrorNotification({
|
showErrorNotification({
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Button, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
|
import { Button, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
import { IconId, IconLock, IconSettings, IconUsersGroup } from "@tabler/icons-react";
|
import { IconId, IconLock, IconSettings, IconUsersGroup } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
import { NavigationLink } from "./_navigation";
|
import { NavigationLink } from "./_navigation";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { Alert, Anchor, Center, Group, Stack, Table, TableTbody, TableTd, TableTr, Text, Title } from "@mantine/core";
|
import { Alert, Anchor, Center, Group, Stack, Table, TableTbody, TableTd, TableTr, Text, Title } from "@mantine/core";
|
||||||
import { IconExclamationCircle } from "@tabler/icons-react";
|
import { IconExclamationCircle } from "@tabler/icons-react";
|
||||||
@@ -9,7 +10,7 @@ import { auth } from "@homarr/auth/next";
|
|||||||
import { isProviderEnabled } from "@homarr/auth/server";
|
import { isProviderEnabled } from "@homarr/auth/server";
|
||||||
import { everyoneGroup } from "@homarr/definitions";
|
import { everyoneGroup } from "@homarr/definitions";
|
||||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||||
import { Link, SearchInput, UserAvatar } from "@homarr/ui";
|
import { SearchInput, UserAvatar } from "@homarr/ui";
|
||||||
|
|
||||||
import { ReservedGroupAlert } from "../_reserved-group-alert";
|
import { ReservedGroupAlert } from "../_reserved-group-alert";
|
||||||
import { AddGroupMember } from "./_add-group-member";
|
import { AddGroupMember } from "./_add-group-member";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import type { DragEndEvent, DraggableAttributes, DragStartEvent } from "@dnd-kit/core";
|
import type { DragEndEvent, DraggableAttributes, DragStartEvent } from "@dnd-kit/core";
|
||||||
import {
|
import {
|
||||||
closestCenter,
|
closestCenter,
|
||||||
@@ -40,7 +41,7 @@ import { clientApi } from "@homarr/api/client";
|
|||||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||||
import { showSuccessNotification } from "@homarr/notifications";
|
import { showSuccessNotification } from "@homarr/notifications";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { Link, UserAvatarGroup } from "@homarr/ui";
|
import { UserAvatarGroup } from "@homarr/ui";
|
||||||
|
|
||||||
interface GroupsTableProps {
|
interface GroupsTableProps {
|
||||||
initialGroupIds: string[];
|
initialGroupIds: string[];
|
||||||
@@ -56,19 +57,16 @@ export const GroupsTable = ({ groups, initialGroupIds, hasFilter }: GroupsTableP
|
|||||||
() => initialGroupIds.some((groupId, index) => groupIds.indexOf(groupId) !== index),
|
() => initialGroupIds.some((groupId, index) => groupIds.indexOf(groupId) !== index),
|
||||||
[groupIds, initialGroupIds],
|
[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 () => {
|
const handleSavePositionsAsync = async () => {
|
||||||
await mutateAsync(
|
await mutateAsync(
|
||||||
{ positions: groupIds },
|
{ positions: groupIds },
|
||||||
{
|
{
|
||||||
onSuccess() {
|
async onSuccess() {
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
message: t("group.action.changePosition.notification.success.message"),
|
message: t("group.action.changePosition.notification.success.message"),
|
||||||
});
|
});
|
||||||
|
await revalidatePathActionAsync("/manage/users/groups");
|
||||||
},
|
},
|
||||||
onError() {
|
onError() {
|
||||||
showSuccessNotification({
|
showSuccessNotification({
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { Card, Group, Stack, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
|
import { Card, Group, Stack, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
|
||||||
import { IconChevronRight, IconUsersGroup } from "@tabler/icons-react";
|
import { IconChevronRight, IconUsersGroup } from "@tabler/icons-react";
|
||||||
@@ -6,7 +7,6 @@ import { api } from "@homarr/api/server";
|
|||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { everyoneGroup } from "@homarr/definitions";
|
import { everyoneGroup } from "@homarr/definitions";
|
||||||
import { getI18n } from "@homarr/translation/server";
|
import { getI18n } from "@homarr/translation/server";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||||
|
|||||||
@@ -6,12 +6,9 @@ import { appRouter, createTRPCContext } from "@homarr/api";
|
|||||||
import type { Session } from "@homarr/auth";
|
import type { Session } from "@homarr/auth";
|
||||||
import { hashPasswordAsync } from "@homarr/auth";
|
import { hashPasswordAsync } from "@homarr/auth";
|
||||||
import { createSessionAsync } from "@homarr/auth/server";
|
import { createSessionAsync } from "@homarr/auth/server";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
|
||||||
import { db, eq } from "@homarr/db";
|
import { db, eq } from "@homarr/db";
|
||||||
import { apiKeys } from "@homarr/db/schema";
|
import { apiKeys } from "@homarr/db/schema";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
const logger = createLogger({ module: "trpcOpenApiRoute" });
|
|
||||||
|
|
||||||
const handlerAsync = async (req: NextRequest) => {
|
const handlerAsync = async (req: NextRequest) => {
|
||||||
const apiKeyHeaderValue = req.headers.get("ApiKey");
|
const apiKeyHeaderValue = req.headers.get("ApiKey");
|
||||||
@@ -30,7 +27,7 @@ const handlerAsync = async (req: NextRequest) => {
|
|||||||
router: appRouter,
|
router: appRouter,
|
||||||
createContext: () => createTRPCContext({ session, headers: req.headers }),
|
createContext: () => createTRPCContext({ session, headers: req.headers }),
|
||||||
onError({ error, path, type }) {
|
onError({ error, path, type }) {
|
||||||
logger.error(new ErrorWithMetadata("tRPC Error occured", { path, type }, { cause: error }));
|
logger.error(new Error(`tRPC Error with ${type} on '${path}'`, { cause: error.cause }));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -51,10 +48,9 @@ const getSessionOrDefaultFromHeadersAsync = async (
|
|||||||
const [apiKeyId, apiKey] = apiKeyHeaderValue.split(".");
|
const [apiKeyId, apiKey] = apiKeyHeaderValue.split(".");
|
||||||
|
|
||||||
if (!apiKeyId || !apiKey) {
|
if (!apiKeyId || !apiKey) {
|
||||||
logger.warn("An attempt to authenticate over API has failed due to invalid API key format", {
|
logger.warn(
|
||||||
ipAdress,
|
`An attempt to authenticate over API has failed due to invalid API key format ip='${ipAdress}' userAgent='${userAgent}'`,
|
||||||
userAgent,
|
);
|
||||||
});
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,21 +74,18 @@ const getSessionOrDefaultFromHeadersAsync = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!apiKeyFromDb) {
|
if (!apiKeyFromDb) {
|
||||||
logger.warn("An attempt to authenticate over API has failed", { ipAdress, userAgent });
|
logger.warn(`An attempt to authenticate over API has failed ip='${ipAdress}' userAgent='${userAgent}'`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedApiKey = await hashPasswordAsync(apiKey, apiKeyFromDb.salt);
|
const hashedApiKey = await hashPasswordAsync(apiKey, apiKeyFromDb.salt);
|
||||||
|
|
||||||
if (apiKeyFromDb.apiKey !== hashedApiKey) {
|
if (apiKeyFromDb.apiKey !== hashedApiKey) {
|
||||||
logger.warn("An attempt to authenticate over API has failed", { ipAdress, userAgent });
|
logger.warn(`An attempt to authenticate over API has failed ip='${ipAdress}' userAgent='${userAgent}'`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Read session from API request and found user", {
|
logger.info(`Read session from API request and found user ${apiKeyFromDb.user.name} (${apiKeyFromDb.user.id})`);
|
||||||
name: apiKeyFromDb.user.name,
|
|
||||||
id: apiKeyFromDb.user.id,
|
|
||||||
});
|
|
||||||
return await createSessionAsync(db, apiKeyFromDb.user);
|
return await createSessionAsync(db, apiKeyFromDb.user);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
import { createHandlersAsync } from "@homarr/auth";
|
import { createHandlersAsync } from "@homarr/auth";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import type { SupportedAuthProvider } from "@homarr/definitions";
|
import type { SupportedAuthProvider } from "@homarr/definitions";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
const logger = createLogger({ module: "nextAuthRoute" });
|
|
||||||
|
|
||||||
export const GET = async (req: NextRequest) => {
|
export const GET = async (req: NextRequest) => {
|
||||||
const { handlers } = await createHandlersAsync(extractProvider(req), isSecureCookieEnabled(req));
|
const { handlers } = await createHandlersAsync(extractProvider(req), isSecureCookieEnabled(req));
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import { performance } from "perf_hooks";
|
import { performance } from "perf_hooks";
|
||||||
|
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
|
||||||
import { db } from "@homarr/db";
|
import { db } from "@homarr/db";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
import { handshakeAsync } from "@homarr/redis";
|
import { handshakeAsync } from "@homarr/redis";
|
||||||
|
|
||||||
const logger = createLogger({ module: "healthLiveRoute" });
|
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const timeBeforeHealthCheck = performance.now();
|
const timeBeforeHealthCheck = performance.now();
|
||||||
const response = await executeAndAggregateAllHealthChecksAsync();
|
const response = await executeAndAggregateAllHealthChecksAsync();
|
||||||
logger.info("Completed healthcheck", { elapsed: `${performance.now() - timeBeforeHealthCheck}ms` });
|
logger.info(`Completed healthcheck after ${performance.now() - timeBeforeHealthCheck}ms`);
|
||||||
|
|
||||||
if (response.status === "healthy") {
|
if (response.status === "healthy") {
|
||||||
return new Response(JSON.stringify(response), {
|
return new Response(JSON.stringify(response), {
|
||||||
@@ -76,7 +73,7 @@ const executeHealthCheckSafelyAsync = async (
|
|||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
logger.error(new ErrorWithMetadata("Healthcheck failed", { name }, { cause: error }));
|
logger.error(`Healthcheck '${name}' has failed: ${error}`);
|
||||||
return {
|
return {
|
||||||
status: "unhealthy",
|
status: "unhealthy",
|
||||||
values: {
|
values: {
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|||||||
import { appRouter, createTRPCContext } from "@homarr/api";
|
import { appRouter, createTRPCContext } from "@homarr/api";
|
||||||
import { trpcPath } from "@homarr/api/shared";
|
import { trpcPath } from "@homarr/api/shared";
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { logger } from "@homarr/log";
|
||||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
|
||||||
|
|
||||||
const logger = createLogger({ module: "trpcRoute" });
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure basic CORS headers
|
* Configure basic CORS headers
|
||||||
@@ -34,7 +31,7 @@ const handler = auth(async (req) => {
|
|||||||
req,
|
req,
|
||||||
createContext: () => createTRPCContext({ session: req.auth, headers: req.headers }),
|
createContext: () => createTRPCContext({ session: req.auth, headers: req.headers }),
|
||||||
onError({ error, path, type }) {
|
onError({ error, path, type }) {
|
||||||
logger.error(new ErrorWithMetadata("tRPC Error occured", { path, type }, { cause: error }));
|
logger.error(new Error(`tRPC Error with ${type} on '${path}'`, { cause: error.cause }));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
import DOMPurify from "isomorphic-dompurify";
|
|
||||||
|
|
||||||
import { db, eq } from "@homarr/db";
|
import { db, eq } from "@homarr/db";
|
||||||
import { medias } from "@homarr/db/schema";
|
import { medias } from "@homarr/db/schema";
|
||||||
@@ -20,24 +19,11 @@ export async function GET(_req: NextRequest, props: { params: Promise<{ id: stri
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = new Uint8Array(image.content);
|
|
||||||
|
|
||||||
// Sanitize SVG content to prevent XSS attacks
|
|
||||||
if (image.contentType === "image/svg+xml" || image.contentType === "image/svg") {
|
|
||||||
const svgText = new TextDecoder().decode(content);
|
|
||||||
const sanitized = DOMPurify.sanitize(svgText, {
|
|
||||||
USE_PROFILES: { svg: true, svgFilters: true },
|
|
||||||
});
|
|
||||||
content = new TextEncoder().encode(sanitized);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.set("Content-Type", image.contentType);
|
headers.set("Content-Type", image.contentType);
|
||||||
headers.set("Content-Length", content.length.toString());
|
headers.set("Content-Length", image.content.length.toString());
|
||||||
headers.set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox");
|
|
||||||
headers.set("X-Content-Type-Options", "nosniff");
|
|
||||||
|
|
||||||
return new NextResponse(content, {
|
return new NextResponse(new Uint8Array(image.content), {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Anchor, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
|
import { Anchor, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
|
||||||
import { IconPlus } from "@tabler/icons-react";
|
import { IconPlus } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { useModalAction } from "@homarr/modals";
|
import { useModalAction } from "@homarr/modals";
|
||||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import type { AccessQueryData } from "./access-settings";
|
import type { AccessQueryData } from "./access-settings";
|
||||||
import { AccessSelectRow } from "./access-table-rows";
|
import { AccessSelectRow } from "./access-table-rows";
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { Anchor, Box, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
|
import { Anchor, Box, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
|
||||||
import { IconPlus } from "@tabler/icons-react";
|
import { IconPlus } from "@tabler/icons-react";
|
||||||
|
|
||||||
import { useModalAction } from "@homarr/modals";
|
import { useModalAction } from "@homarr/modals";
|
||||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||||
import { Link, UserAvatar } from "@homarr/ui";
|
import { UserAvatar } from "@homarr/ui";
|
||||||
|
|
||||||
import type { AccessQueryData } from "./access-settings";
|
import type { AccessQueryData } from "./access-settings";
|
||||||
import { AccessDisplayRow, AccessSelectRow } from "./access-table-rows";
|
import { AccessDisplayRow, AccessSelectRow } from "./access-table-rows";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { AppShellHeader, Group, UnstyledButton } from "@mantine/core";
|
import { AppShellHeader, Group, UnstyledButton } from "@mantine/core";
|
||||||
|
|
||||||
import { Spotlight } from "@homarr/spotlight";
|
import { Spotlight } from "@homarr/spotlight";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { ClientBurger } from "./header/burger";
|
import { ClientBurger } from "./header/burger";
|
||||||
import { DesktopSearchInput, MobileSearchButton } from "./header/search";
|
import { DesktopSearchInput, MobileSearchButton } from "./header/search";
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { ForwardedRef, ReactNode } from "react";
|
import type { ForwardedRef, ReactNode } from "react";
|
||||||
import { forwardRef } from "react";
|
import { forwardRef } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import type { ActionIconProps } from "@mantine/core";
|
import type { ActionIconProps } from "@mantine/core";
|
||||||
import { ActionIcon } from "@mantine/core";
|
import { ActionIcon } from "@mantine/core";
|
||||||
|
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
type HeaderButtonProps = (
|
type HeaderButtonProps = (
|
||||||
| {
|
| {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { NavLink } from "@mantine/core";
|
import { NavLink } from "@mantine/core";
|
||||||
|
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
export const CommonNavLink = (props: ClientNavigationLink) =>
|
export const CommonNavLink = (props: ClientNavigationLink) =>
|
||||||
"href" in props ? <NavLinkHref {...props} /> : <NavLinkWithItems {...props} />;
|
"href" in props ? <NavLinkHref {...props} /> : <NavLinkWithItems {...props} />;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { useCallback, useEffect } from "react";
|
import { useCallback, useEffect } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core";
|
import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core";
|
||||||
import { useHotkeys, useTimeout } from "@mantine/hooks";
|
import { useHotkeys, useTimeout } from "@mantine/hooks";
|
||||||
@@ -21,7 +22,6 @@ import { signOut, useSession } from "@homarr/auth/client";
|
|||||||
import { hotkeys } from "@homarr/definitions";
|
import { hotkeys } from "@homarr/definitions";
|
||||||
import { createModal, useModalAction } from "@homarr/modals";
|
import { createModal, useModalAction } from "@homarr/modals";
|
||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
import { Link } from "@homarr/ui";
|
|
||||||
|
|
||||||
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
|
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
|
||||||
import { CurrentLanguageCombobox } from "./language/current-language-combobox";
|
import { CurrentLanguageCombobox } from "./language/current-language-combobox";
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import "server-only";
|
|||||||
import { notFound, redirect } from "next/navigation";
|
import { notFound, redirect } from "next/navigation";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { logger } from "@homarr/log";
|
||||||
|
|
||||||
const logger = createLogger({ module: "trpcCatchError" });
|
|
||||||
|
|
||||||
export const catchTrpcNotFound = (err: unknown) => {
|
export const catchTrpcNotFound = (err: unknown) => {
|
||||||
if (err instanceof TRPCError && err.code === "NOT_FOUND") {
|
if (err instanceof TRPCError && err.code === "NOT_FOUND") {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import type { NextRequest } from "next/server";
|
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import type { NextRequest } from "next/server";
|
||||||
|
import { createTRPCClient, httpLink } from "@trpc/client";
|
||||||
|
import SuperJSON from "superjson";
|
||||||
|
|
||||||
import { api } from "@homarr/api/server";
|
import type { AppRouter } from "@homarr/api";
|
||||||
|
import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared";
|
||||||
import { localeCookieKey } from "@homarr/definitions";
|
import { localeCookieKey } from "@homarr/definitions";
|
||||||
import type { SupportedLanguage } from "@homarr/translation";
|
import type { SupportedLanguage } from "@homarr/translation";
|
||||||
import { supportedLanguages } from "@homarr/translation";
|
import { supportedLanguages } from "@homarr/translation";
|
||||||
@@ -9,12 +12,12 @@ import { createI18nMiddleware } from "@homarr/translation/middleware";
|
|||||||
|
|
||||||
let isOnboardingFinished = false;
|
let isOnboardingFinished = false;
|
||||||
|
|
||||||
export async function proxy(request: NextRequest) {
|
export async function middleware(request: NextRequest) {
|
||||||
// Redirect to onboarding if it's not finished yet
|
// Redirect to onboarding if it's not finished yet
|
||||||
const pathname = request.nextUrl.pathname;
|
const pathname = request.nextUrl.pathname;
|
||||||
|
|
||||||
if (!isOnboardingFinished && !pathname.endsWith("/init")) {
|
if (!isOnboardingFinished && !pathname.endsWith("/init")) {
|
||||||
const currentOnboardingStep = await api.onboard.currentStep();
|
const currentOnboardingStep = await serverFetchApi.onboard.currentStep.query();
|
||||||
if (currentOnboardingStep.current !== "finish") {
|
if (currentOnboardingStep.current !== "finish") {
|
||||||
return NextResponse.redirect(new URL("/init", request.url));
|
return NextResponse.redirect(new URL("/init", request.url));
|
||||||
}
|
}
|
||||||
@@ -26,12 +29,11 @@ export async function proxy(request: NextRequest) {
|
|||||||
const currentLocale = request.cookies.get(localeCookieKey)?.value;
|
const currentLocale = request.cookies.get(localeCookieKey)?.value;
|
||||||
let defaultLocale: SupportedLanguage = "en";
|
let defaultLocale: SupportedLanguage = "en";
|
||||||
if (!currentLocale || !supportedLanguages.includes(currentLocale as SupportedLanguage)) {
|
if (!currentLocale || !supportedLanguages.includes(currentLocale as SupportedLanguage)) {
|
||||||
defaultLocale = await api.serverSettings.getCulture().then((culture) => culture.defaultLocale);
|
defaultLocale = await serverFetchApi.serverSettings.getCulture.query().then((culture) => culture.defaultLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't want to fallback to accept-language header so we clear it
|
// We don't want to fallback to accept-language header so we clear it
|
||||||
request.headers.set("accept-language", "");
|
request.headers.set("accept-language", "");
|
||||||
|
|
||||||
const next = createI18nMiddleware(defaultLocale);
|
const next = createI18nMiddleware(defaultLocale);
|
||||||
return next(request);
|
return next(request);
|
||||||
}
|
}
|
||||||
@@ -39,3 +41,13 @@ export async function proxy(request: NextRequest) {
|
|||||||
export const config = {
|
export const config = {
|
||||||
matcher: ["/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
|
matcher: ["/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const serverFetchApi = createTRPCClient<AppRouter>({
|
||||||
|
links: [
|
||||||
|
httpLink({
|
||||||
|
url: getTrpcUrl(),
|
||||||
|
transformer: SuperJSON,
|
||||||
|
headers: createHeadersCallbackForSource("server-fetch"),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
"@homarr/analytics": "workspace:^0.1.0",
|
"@homarr/analytics": "workspace:^0.1.0",
|
||||||
"@homarr/auth": "workspace:^0.1.0",
|
"@homarr/auth": "workspace:^0.1.0",
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/core": "workspace:^",
|
|
||||||
"@homarr/cron-job-api": "workspace:^0.1.0",
|
"@homarr/cron-job-api": "workspace:^0.1.0",
|
||||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||||
"@homarr/cron-jobs-core": "workspace:^0.1.0",
|
"@homarr/cron-jobs-core": "workspace:^0.1.0",
|
||||||
@@ -31,27 +30,28 @@
|
|||||||
"@homarr/definitions": "workspace:^0.1.0",
|
"@homarr/definitions": "workspace:^0.1.0",
|
||||||
"@homarr/icons": "workspace:^0.1.0",
|
"@homarr/icons": "workspace:^0.1.0",
|
||||||
"@homarr/integrations": "workspace:^0.1.0",
|
"@homarr/integrations": "workspace:^0.1.0",
|
||||||
|
"@homarr/log": "workspace:^",
|
||||||
"@homarr/ping": "workspace:^0.1.0",
|
"@homarr/ping": "workspace:^0.1.0",
|
||||||
"@homarr/redis": "workspace:^0.1.0",
|
"@homarr/redis": "workspace:^0.1.0",
|
||||||
"@homarr/request-handler": "workspace:^0.1.0",
|
"@homarr/request-handler": "workspace:^0.1.0",
|
||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@homarr/widgets": "workspace:^0.1.0",
|
"@homarr/widgets": "workspace:^0.1.0",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.18",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.1",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.3",
|
||||||
"undici": "7.16.0"
|
"undici": "7.16.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/node": "^24.10.4",
|
"@types/node": "^24.9.1",
|
||||||
"dotenv-cli": "^11.0.0",
|
"dotenv-cli": "^10.0.0",
|
||||||
"esbuild": "^0.27.1",
|
"esbuild": "^0.25.11",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.38.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.6.2",
|
||||||
"tsx": "4.20.4",
|
"tsx": "4.20.4",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { schedule, validate as validateCron } from "node-cron";
|
import { schedule, validate as validateCron } from "node-cron";
|
||||||
|
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import type { IJobManager } from "@homarr/cron-job-api";
|
import type { IJobManager } from "@homarr/cron-job-api";
|
||||||
import type { jobGroup as cronJobGroup, JobGroupKeys } from "@homarr/cron-jobs";
|
import type { jobGroup as cronJobGroup, JobGroupKeys } from "@homarr/cron-jobs";
|
||||||
import type { Database, InferInsertModel } from "@homarr/db";
|
import type { Database, InferInsertModel } from "@homarr/db";
|
||||||
import { eq } from "@homarr/db";
|
import { eq } from "@homarr/db";
|
||||||
import { cronJobConfigurations } from "@homarr/db/schema";
|
import { cronJobConfigurations } from "@homarr/db/schema";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
const logger = createLogger({ module: "jobManager" });
|
|
||||||
|
|
||||||
export class JobManager implements IJobManager {
|
export class JobManager implements IJobManager {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -25,7 +23,7 @@ export class JobManager implements IJobManager {
|
|||||||
await this.jobGroup.stopAsync(name);
|
await this.jobGroup.stopAsync(name);
|
||||||
}
|
}
|
||||||
public async updateIntervalAsync(name: JobGroupKeys, cron: string): Promise<void> {
|
public async updateIntervalAsync(name: JobGroupKeys, cron: string): Promise<void> {
|
||||||
logger.info("Updating cron job interval", { name, expression: cron });
|
logger.info(`Updating cron job interval name="${name}" expression="${cron}"`);
|
||||||
const job = this.jobGroup.getJobRegistry().get(name);
|
const job = this.jobGroup.getJobRegistry().get(name);
|
||||||
if (!job) throw new Error(`Job ${name} not found`);
|
if (!job) throw new Error(`Job ${name} not found`);
|
||||||
if (!validateCron(cron)) {
|
if (!validateCron(cron)) {
|
||||||
@@ -40,22 +38,22 @@ export class JobManager implements IJobManager {
|
|||||||
name,
|
name,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
logger.info("Cron job interval updated", { name, expression: cron });
|
logger.info(`Cron job interval updated name="${name}" expression="${cron}"`);
|
||||||
}
|
}
|
||||||
public async disableAsync(name: JobGroupKeys): Promise<void> {
|
public async disableAsync(name: JobGroupKeys): Promise<void> {
|
||||||
logger.info("Disabling cron job", { name });
|
logger.info(`Disabling cron job name="${name}"`);
|
||||||
const job = this.jobGroup.getJobRegistry().get(name);
|
const job = this.jobGroup.getJobRegistry().get(name);
|
||||||
if (!job) throw new Error(`Job ${name} not found`);
|
if (!job) throw new Error(`Job ${name} not found`);
|
||||||
|
|
||||||
await this.updateConfigurationAsync(name, { isEnabled: false });
|
await this.updateConfigurationAsync(name, { isEnabled: false });
|
||||||
await this.jobGroup.stopAsync(name);
|
await this.jobGroup.stopAsync(name);
|
||||||
logger.info("Cron job disabled", { name });
|
logger.info(`Cron job disabled name="${name}"`);
|
||||||
}
|
}
|
||||||
public async enableAsync(name: JobGroupKeys): Promise<void> {
|
public async enableAsync(name: JobGroupKeys): Promise<void> {
|
||||||
logger.info("Enabling cron job", { name });
|
logger.info(`Enabling cron job name="${name}"`);
|
||||||
await this.updateConfigurationAsync(name, { isEnabled: true });
|
await this.updateConfigurationAsync(name, { isEnabled: true });
|
||||||
await this.jobGroup.startAsync(name);
|
await this.jobGroup.startAsync(name);
|
||||||
logger.info("Cron job enabled", { name });
|
logger.info(`Cron job enabled name="${name}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateConfigurationAsync(
|
private async updateConfigurationAsync(
|
||||||
@@ -66,11 +64,9 @@ export class JobManager implements IJobManager {
|
|||||||
where: (table, { eq }) => eq(table.name, name),
|
where: (table, { eq }) => eq(table.name, name),
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug("Updating cron job configuration", {
|
logger.debug(
|
||||||
name,
|
`Updating cron job configuration name="${name}" configuration="${JSON.stringify(configuration)}" exists="${Boolean(existingConfig)}"`,
|
||||||
configuration: JSON.stringify(configuration),
|
);
|
||||||
exists: Boolean(existingConfig),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingConfig) {
|
if (existingConfig) {
|
||||||
await this.db
|
await this.db
|
||||||
@@ -78,10 +74,7 @@ export class JobManager implements IJobManager {
|
|||||||
// prevent updating the name, as it is the primary key
|
// prevent updating the name, as it is the primary key
|
||||||
.set({ ...configuration, name: undefined })
|
.set({ ...configuration, name: undefined })
|
||||||
.where(eq(cronJobConfigurations.name, name));
|
.where(eq(cronJobConfigurations.name, name));
|
||||||
logger.debug("Cron job configuration updated", {
|
logger.debug(`Cron job configuration updated name="${name}" configuration="${JSON.stringify(configuration)}"`);
|
||||||
name,
|
|
||||||
configuration: JSON.stringify(configuration),
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +86,7 @@ export class JobManager implements IJobManager {
|
|||||||
cronExpression: configuration.cronExpression ?? job.cronExpression,
|
cronExpression: configuration.cronExpression ?? job.cronExpression,
|
||||||
isEnabled: configuration.isEnabled ?? true,
|
isEnabled: configuration.isEnabled ?? true,
|
||||||
});
|
});
|
||||||
logger.debug("Cron job configuration updated", {
|
logger.debug(`Cron job configuration updated name="${name}" configuration="${JSON.stringify(configuration)}"`);
|
||||||
name,
|
|
||||||
configuration: JSON.stringify(configuration),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAllAsync(): Promise<
|
public async getAllAsync(): Promise<
|
||||||
|
|||||||
@@ -5,19 +5,16 @@ import type { FastifyTRPCPluginOptions } from "@trpc/server/adapters/fastify";
|
|||||||
import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify";
|
import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify";
|
||||||
import fastify from "fastify";
|
import fastify from "fastify";
|
||||||
|
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
|
||||||
import type { JobRouter } from "@homarr/cron-job-api";
|
import type { JobRouter } from "@homarr/cron-job-api";
|
||||||
import { jobRouter } from "@homarr/cron-job-api";
|
import { jobRouter } from "@homarr/cron-job-api";
|
||||||
import { CRON_JOB_API_KEY_HEADER, CRON_JOB_API_PATH, CRON_JOB_API_PORT } from "@homarr/cron-job-api/constants";
|
import { CRON_JOB_API_KEY_HEADER, CRON_JOB_API_PATH, CRON_JOB_API_PORT } from "@homarr/cron-job-api/constants";
|
||||||
import { jobGroup } from "@homarr/cron-jobs";
|
import { jobGroup } from "@homarr/cron-jobs";
|
||||||
import { db } from "@homarr/db";
|
import { db } from "@homarr/db";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
|
|
||||||
import { JobManager } from "./job-manager";
|
import { JobManager } from "./job-manager";
|
||||||
import { onStartAsync } from "./on-start";
|
import { onStartAsync } from "./on-start";
|
||||||
|
|
||||||
const logger = createLogger({ module: "tasksMain" });
|
|
||||||
|
|
||||||
const server = fastify({
|
const server = fastify({
|
||||||
maxParamLength: 5000,
|
maxParamLength: 5000,
|
||||||
});
|
});
|
||||||
@@ -30,7 +27,7 @@ server.register(fastifyTRPCPlugin, {
|
|||||||
apiKey: req.headers[CRON_JOB_API_KEY_HEADER] as string | undefined,
|
apiKey: req.headers[CRON_JOB_API_KEY_HEADER] as string | undefined,
|
||||||
}),
|
}),
|
||||||
onError({ path, error }) {
|
onError({ path, error }) {
|
||||||
logger.error(new ErrorWithMetadata("Error in tasks tRPC handler", { path }, { cause: error }));
|
logger.error(new Error(`Error in tasks tRPC handler path="${path}"`, { cause: error }));
|
||||||
},
|
},
|
||||||
} satisfies FastifyTRPCPluginOptions<JobRouter>["trpcOptions"],
|
} satisfies FastifyTRPCPluginOptions<JobRouter>["trpcOptions"],
|
||||||
});
|
});
|
||||||
@@ -42,11 +39,9 @@ void (async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await server.listen({ port: CRON_JOB_API_PORT });
|
await server.listen({ port: CRON_JOB_API_PORT });
|
||||||
logger.info("Tasks web server started successfully", { port: CRON_JOB_API_PORT });
|
logger.info(`Tasks web server started successfully port="${CRON_JOB_API_PORT}"`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error(new Error(`Failed to start tasks web server port="${CRON_JOB_API_PORT}"`, { cause: err }));
|
||||||
new ErrorWithMetadata("Failed to start tasks web server", { port: CRON_JOB_API_PORT }, { cause: err }),
|
|
||||||
);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { logger } from "@homarr/log";
|
||||||
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";
|
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";
|
||||||
|
|
||||||
const logger = createLogger({ module: "invalidateUpdateCheckerCache" });
|
const localLogger = logger.child({ module: "invalidateUpdateCheckerCache" });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidates the update checker cache on startup to ensure fresh data.
|
* Invalidates the update checker cache on startup to ensure fresh data.
|
||||||
@@ -11,8 +11,8 @@ export async function invalidateUpdateCheckerCacheAsync() {
|
|||||||
try {
|
try {
|
||||||
const handler = updateCheckerRequestHandler.handler({});
|
const handler = updateCheckerRequestHandler.handler({});
|
||||||
await handler.invalidateAsync();
|
await handler.invalidateAsync();
|
||||||
logger.debug("Update checker cache invalidated");
|
localLogger.debug("Update checker cache invalidated");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(new Error("Failed to invalidate update checker cache", { cause: error }));
|
localLogger.error(new Error("Failed to invalidate update checker cache", { cause: error }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { env } from "@homarr/auth/env";
|
import { env } from "@homarr/auth/env";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { db, eq, inArray } from "@homarr/db";
|
import { db, eq, inArray } from "@homarr/db";
|
||||||
import { sessions, users } from "@homarr/db/schema";
|
import { sessions, users } from "@homarr/db/schema";
|
||||||
import { supportedAuthProviders } from "@homarr/definitions";
|
import { supportedAuthProviders } from "@homarr/definitions";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
|
|
||||||
const logger = createLogger({ module: "sessionCleanup" });
|
const localLogger = logger.child({ module: "sessionCleanup" });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes sessions for users that have inactive auth providers.
|
* Deletes sessions for users that have inactive auth providers.
|
||||||
@@ -29,13 +29,11 @@ export async function cleanupSessionsAsync() {
|
|||||||
await db.delete(sessions).where(inArray(sessions.userId, userIds));
|
await db.delete(sessions).where(inArray(sessions.userId, userIds));
|
||||||
|
|
||||||
if (sessionsWithInactiveProviders.length > 0) {
|
if (sessionsWithInactiveProviders.length > 0) {
|
||||||
logger.info("Deleted sessions for inactive providers", {
|
localLogger.info(`Deleted sessions for inactive providers count=${userIds.length}`);
|
||||||
count: userIds.length,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
logger.debug("No sessions to delete");
|
localLogger.debug("No sessions to delete");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(new Error("Failed to clean up sessions", { cause: error }));
|
localLogger.error(new Error("Failed to clean up sessions", { cause: error }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { setGlobalDispatcher } from "undici";
|
import { setGlobalDispatcher } from "undici";
|
||||||
|
|
||||||
import { UndiciHttpAgent } from "@homarr/core/infrastructure/http";
|
import { LoggingAgent } from "@homarr/common/server";
|
||||||
|
|
||||||
setGlobalDispatcher(new UndiciHttpAgent());
|
const agent = new LoggingAgent();
|
||||||
|
setGlobalDispatcher(agent);
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
"@homarr/api": "workspace:^0.1.0",
|
"@homarr/api": "workspace:^0.1.0",
|
||||||
"@homarr/auth": "workspace:^0.1.0",
|
"@homarr/auth": "workspace:^0.1.0",
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/core": "workspace:^",
|
|
||||||
"@homarr/db": "workspace:^0.1.0",
|
"@homarr/db": "workspace:^0.1.0",
|
||||||
"@homarr/definitions": "workspace:^0.1.0",
|
"@homarr/definitions": "workspace:^0.1.0",
|
||||||
|
"@homarr/log": "workspace:^",
|
||||||
"@homarr/redis": "workspace:^0.1.0",
|
"@homarr/redis": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
@@ -34,9 +34,9 @@
|
|||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
"esbuild": "^0.27.1",
|
"esbuild": "^0.25.11",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.38.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,8 @@ import { WebSocketServer } from "ws";
|
|||||||
import { appRouter, createTRPCContext } from "@homarr/api/websocket";
|
import { appRouter, createTRPCContext } from "@homarr/api/websocket";
|
||||||
import { getSessionFromToken, sessionTokenCookieName } from "@homarr/auth";
|
import { getSessionFromToken, sessionTokenCookieName } from "@homarr/auth";
|
||||||
import { parseCookies } from "@homarr/common";
|
import { parseCookies } from "@homarr/common";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { db } from "@homarr/db";
|
import { db } from "@homarr/db";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
const logger = createLogger({ module: "websocketMain" });
|
|
||||||
|
|
||||||
const wss = new WebSocketServer({
|
const wss = new WebSocketServer({
|
||||||
port: 3001,
|
port: 3001,
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
FROM node:24.12.0-trixie AS base
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package.json .
|
|
||||||
COPY pnpm-lock.yaml .
|
|
||||||
RUN corepack enable pnpm && pnpm install --frozen-lockfile
|
|
||||||
CMD ["sleep", "60s"]
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "homarr-prebuilt-debian",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"better-sqlite3": "^12.5.0"
|
|
||||||
},
|
|
||||||
"packageManager": "pnpm@10.25.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=24.12.0",
|
|
||||||
"pnpm": ">=10.25.0"
|
|
||||||
},
|
|
||||||
"pnpm": {
|
|
||||||
"onlyBuiltDependencies": [
|
|
||||||
"better-sqlite3"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-286
@@ -1,286 +0,0 @@
|
|||||||
lockfileVersion: '9.0'
|
|
||||||
|
|
||||||
settings:
|
|
||||||
autoInstallPeers: true
|
|
||||||
excludeLinksFromLockfile: false
|
|
||||||
|
|
||||||
importers:
|
|
||||||
|
|
||||||
.:
|
|
||||||
dependencies:
|
|
||||||
better-sqlite3:
|
|
||||||
specifier: ^12.5.0
|
|
||||||
version: 12.5.0
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
base64-js@1.5.1:
|
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
|
||||||
|
|
||||||
better-sqlite3@12.5.0:
|
|
||||||
resolution: {integrity: sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==}
|
|
||||||
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
|
|
||||||
|
|
||||||
bindings@1.5.0:
|
|
||||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
|
||||||
|
|
||||||
bl@4.1.0:
|
|
||||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
|
||||||
|
|
||||||
buffer@5.7.1:
|
|
||||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
|
||||||
|
|
||||||
chownr@1.1.4:
|
|
||||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
|
||||||
|
|
||||||
decompress-response@6.0.0:
|
|
||||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
|
|
||||||
deep-extend@0.6.0:
|
|
||||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
|
||||||
engines: {node: '>=4.0.0'}
|
|
||||||
|
|
||||||
detect-libc@2.1.2:
|
|
||||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
|
|
||||||
end-of-stream@1.4.5:
|
|
||||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
|
||||||
|
|
||||||
expand-template@2.0.3:
|
|
||||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
|
|
||||||
file-uri-to-path@1.0.0:
|
|
||||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
|
||||||
|
|
||||||
fs-constants@1.0.0:
|
|
||||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
|
||||||
|
|
||||||
github-from-package@0.0.0:
|
|
||||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
|
||||||
|
|
||||||
ieee754@1.2.1:
|
|
||||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
|
||||||
|
|
||||||
inherits@2.0.4:
|
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
|
||||||
|
|
||||||
ini@1.3.8:
|
|
||||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
|
||||||
|
|
||||||
mimic-response@3.1.0:
|
|
||||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
|
|
||||||
minimist@1.2.8:
|
|
||||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
|
||||||
|
|
||||||
mkdirp-classic@0.5.3:
|
|
||||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
|
||||||
|
|
||||||
napi-build-utils@2.0.0:
|
|
||||||
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
|
||||||
|
|
||||||
node-abi@3.85.0:
|
|
||||||
resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
|
|
||||||
once@1.4.0:
|
|
||||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
|
||||||
|
|
||||||
prebuild-install@7.1.3:
|
|
||||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
pump@3.0.3:
|
|
||||||
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
|
|
||||||
|
|
||||||
rc@1.2.8:
|
|
||||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
readable-stream@3.6.2:
|
|
||||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
|
|
||||||
safe-buffer@5.2.1:
|
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
|
||||||
|
|
||||||
semver@7.7.3:
|
|
||||||
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
simple-concat@1.0.1:
|
|
||||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
|
||||||
|
|
||||||
simple-get@4.0.1:
|
|
||||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
|
||||||
|
|
||||||
string_decoder@1.3.0:
|
|
||||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
|
||||||
|
|
||||||
strip-json-comments@2.0.1:
|
|
||||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
|
|
||||||
tar-fs@2.1.4:
|
|
||||||
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
|
|
||||||
|
|
||||||
tar-stream@2.2.0:
|
|
||||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
|
|
||||||
tunnel-agent@0.6.0:
|
|
||||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
|
||||||
|
|
||||||
util-deprecate@1.0.2:
|
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
|
||||||
|
|
||||||
wrappy@1.0.2:
|
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
|
||||||
|
|
||||||
snapshots:
|
|
||||||
|
|
||||||
base64-js@1.5.1: {}
|
|
||||||
|
|
||||||
better-sqlite3@12.5.0:
|
|
||||||
dependencies:
|
|
||||||
bindings: 1.5.0
|
|
||||||
prebuild-install: 7.1.3
|
|
||||||
|
|
||||||
bindings@1.5.0:
|
|
||||||
dependencies:
|
|
||||||
file-uri-to-path: 1.0.0
|
|
||||||
|
|
||||||
bl@4.1.0:
|
|
||||||
dependencies:
|
|
||||||
buffer: 5.7.1
|
|
||||||
inherits: 2.0.4
|
|
||||||
readable-stream: 3.6.2
|
|
||||||
|
|
||||||
buffer@5.7.1:
|
|
||||||
dependencies:
|
|
||||||
base64-js: 1.5.1
|
|
||||||
ieee754: 1.2.1
|
|
||||||
|
|
||||||
chownr@1.1.4: {}
|
|
||||||
|
|
||||||
decompress-response@6.0.0:
|
|
||||||
dependencies:
|
|
||||||
mimic-response: 3.1.0
|
|
||||||
|
|
||||||
deep-extend@0.6.0: {}
|
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
|
||||||
|
|
||||||
end-of-stream@1.4.5:
|
|
||||||
dependencies:
|
|
||||||
once: 1.4.0
|
|
||||||
|
|
||||||
expand-template@2.0.3: {}
|
|
||||||
|
|
||||||
file-uri-to-path@1.0.0: {}
|
|
||||||
|
|
||||||
fs-constants@1.0.0: {}
|
|
||||||
|
|
||||||
github-from-package@0.0.0: {}
|
|
||||||
|
|
||||||
ieee754@1.2.1: {}
|
|
||||||
|
|
||||||
inherits@2.0.4: {}
|
|
||||||
|
|
||||||
ini@1.3.8: {}
|
|
||||||
|
|
||||||
mimic-response@3.1.0: {}
|
|
||||||
|
|
||||||
minimist@1.2.8: {}
|
|
||||||
|
|
||||||
mkdirp-classic@0.5.3: {}
|
|
||||||
|
|
||||||
napi-build-utils@2.0.0: {}
|
|
||||||
|
|
||||||
node-abi@3.85.0:
|
|
||||||
dependencies:
|
|
||||||
semver: 7.7.3
|
|
||||||
|
|
||||||
once@1.4.0:
|
|
||||||
dependencies:
|
|
||||||
wrappy: 1.0.2
|
|
||||||
|
|
||||||
prebuild-install@7.1.3:
|
|
||||||
dependencies:
|
|
||||||
detect-libc: 2.1.2
|
|
||||||
expand-template: 2.0.3
|
|
||||||
github-from-package: 0.0.0
|
|
||||||
minimist: 1.2.8
|
|
||||||
mkdirp-classic: 0.5.3
|
|
||||||
napi-build-utils: 2.0.0
|
|
||||||
node-abi: 3.85.0
|
|
||||||
pump: 3.0.3
|
|
||||||
rc: 1.2.8
|
|
||||||
simple-get: 4.0.1
|
|
||||||
tar-fs: 2.1.4
|
|
||||||
tunnel-agent: 0.6.0
|
|
||||||
|
|
||||||
pump@3.0.3:
|
|
||||||
dependencies:
|
|
||||||
end-of-stream: 1.4.5
|
|
||||||
once: 1.4.0
|
|
||||||
|
|
||||||
rc@1.2.8:
|
|
||||||
dependencies:
|
|
||||||
deep-extend: 0.6.0
|
|
||||||
ini: 1.3.8
|
|
||||||
minimist: 1.2.8
|
|
||||||
strip-json-comments: 2.0.1
|
|
||||||
|
|
||||||
readable-stream@3.6.2:
|
|
||||||
dependencies:
|
|
||||||
inherits: 2.0.4
|
|
||||||
string_decoder: 1.3.0
|
|
||||||
util-deprecate: 1.0.2
|
|
||||||
|
|
||||||
safe-buffer@5.2.1: {}
|
|
||||||
|
|
||||||
semver@7.7.3: {}
|
|
||||||
|
|
||||||
simple-concat@1.0.1: {}
|
|
||||||
|
|
||||||
simple-get@4.0.1:
|
|
||||||
dependencies:
|
|
||||||
decompress-response: 6.0.0
|
|
||||||
once: 1.4.0
|
|
||||||
simple-concat: 1.0.1
|
|
||||||
|
|
||||||
string_decoder@1.3.0:
|
|
||||||
dependencies:
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
|
|
||||||
strip-json-comments@2.0.1: {}
|
|
||||||
|
|
||||||
tar-fs@2.1.4:
|
|
||||||
dependencies:
|
|
||||||
chownr: 1.1.4
|
|
||||||
mkdirp-classic: 0.5.3
|
|
||||||
pump: 3.0.3
|
|
||||||
tar-stream: 2.2.0
|
|
||||||
|
|
||||||
tar-stream@2.2.0:
|
|
||||||
dependencies:
|
|
||||||
bl: 4.1.0
|
|
||||||
end-of-stream: 1.4.5
|
|
||||||
fs-constants: 1.0.0
|
|
||||||
inherits: 2.0.4
|
|
||||||
readable-stream: 3.6.2
|
|
||||||
|
|
||||||
tunnel-agent@0.6.0:
|
|
||||||
dependencies:
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
|
||||||
@@ -5,7 +5,6 @@ import Database from "better-sqlite3";
|
|||||||
import { BetterSQLite3Database, drizzle } from "drizzle-orm/better-sqlite3";
|
import { BetterSQLite3Database, drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
|
|
||||||
import { DB_CASING } from "../../packages/core/src/infrastructure/db/constants";
|
|
||||||
import * as sqliteSchema from "../../packages/db/schema/sqlite";
|
import * as sqliteSchema from "../../packages/db/schema/sqlite";
|
||||||
|
|
||||||
export const createSqliteDbFileAsync = async () => {
|
export const createSqliteDbFileAsync = async () => {
|
||||||
@@ -17,7 +16,7 @@ export const createSqliteDbFileAsync = async () => {
|
|||||||
const connection = new Database(localDbUrl);
|
const connection = new Database(localDbUrl);
|
||||||
const db = drizzle(connection, {
|
const db = drizzle(connection, {
|
||||||
schema: sqliteSchema,
|
schema: sqliteSchema,
|
||||||
casing: DB_CASING,
|
casing: "snake_case",
|
||||||
});
|
});
|
||||||
|
|
||||||
await migrate(db, {
|
await migrate(db, {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ events {
|
|||||||
http {
|
http {
|
||||||
server {
|
server {
|
||||||
listen 7575;
|
listen 7575;
|
||||||
listen [::]:7575;
|
|
||||||
|
|
||||||
# Route websockets traffic to port 3001
|
# Route websockets traffic to port 3001
|
||||||
location /websockets {
|
location /websockets {
|
||||||
|
|||||||
+24
-25
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homarr",
|
"name": "homarr",
|
||||||
"version": "1.47.0",
|
"version": "1.43.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env CI=true turbo build",
|
"build": "cross-env CI=true turbo build",
|
||||||
@@ -39,35 +39,34 @@
|
|||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"@semantic-release/github": "^12.0.2",
|
"@semantic-release/github": "^12.0.0",
|
||||||
"@semantic-release/npm": "^13.1.3",
|
"@semantic-release/npm": "^13.1.1",
|
||||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||||
"@testcontainers/redis": "^11.10.0",
|
"@testcontainers/redis": "^11.7.2",
|
||||||
"@turbo/gen": "^2.6.3",
|
"@turbo/gen": "^2.5.8",
|
||||||
"@vitejs/plugin-react": "^5.1.2",
|
"@vitejs/plugin-react": "^5.1.0",
|
||||||
"@vitest/coverage-v8": "^4.0.15",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"@vitest/ui": "^4.0.15",
|
"@vitest/ui": "^3.2.4",
|
||||||
"conventional-changelog-conventionalcommits": "^9.1.0",
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"jsdom": "^27.3.0",
|
"jsdom": "^27.0.1",
|
||||||
"json5": "^2.2.3",
|
"prettier": "^3.6.2",
|
||||||
"prettier": "^3.7.4",
|
"semantic-release": "^25.0.1",
|
||||||
"semantic-release": "^25.0.2",
|
"testcontainers": "^11.7.2",
|
||||||
"testcontainers": "^11.10.0",
|
"turbo": "^2.5.8",
|
||||||
"turbo": "^2.6.3",
|
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^4.0.15"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.25.0",
|
"packageManager": "pnpm@10.19.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.12.0",
|
"node": ">=22.21.0"
|
||||||
"pnpm": ">=10.25.0"
|
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@tree-sitter-grammars/tree-sitter-yaml",
|
"@tree-sitter-grammars/tree-sitter-yaml",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
|
"better-sqlite3",
|
||||||
"cpu-features",
|
"cpu-features",
|
||||||
"esbuild",
|
"esbuild",
|
||||||
"sharp",
|
"sharp",
|
||||||
@@ -78,23 +77,23 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"@babel/helpers@<7.26.10": ">=7.28.4",
|
"@babel/helpers@<7.26.10": ">=7.28.4",
|
||||||
"@babel/runtime@<7.26.10": ">=7.28.4",
|
"@babel/runtime@<7.26.10": ">=7.28.4",
|
||||||
"axios@>=1.0.0 <1.8.2": ">=1.13.2",
|
"axios@>=1.0.0 <1.8.2": ">=1.12.2",
|
||||||
"brace-expansion@>=2.0.0 <=2.0.1": ">=4.0.1",
|
"brace-expansion@>=2.0.0 <=2.0.1": ">=4.0.1",
|
||||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
|
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
|
||||||
"esbuild@<=0.24.2": ">=0.27.1",
|
"esbuild@<=0.24.2": ">=0.25.11",
|
||||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
"form-data@>=4.0.0 <4.0.4": ">=4.0.4",
|
||||||
"hono@<4.6.5": ">=4.11.0",
|
"hono@<4.6.5": ">=4.10.3",
|
||||||
"linkifyjs@<4.3.2": ">=4.3.2",
|
"linkifyjs@<4.3.2": ">=4.3.2",
|
||||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
|
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
|
||||||
"prismjs@<1.30.0": ">=1.30.0",
|
"prismjs@<1.30.0": ">=1.30.0",
|
||||||
"proxmox-api>undici": "7.16.0",
|
"proxmox-api>undici": "7.16.0",
|
||||||
"react-is": "^19.2.3",
|
"react-is": "^19.2.0",
|
||||||
"rollup@>=4.0.0 <4.22.4": ">=4.53.3",
|
"rollup@>=4.0.0 <4.22.4": ">=4.52.5",
|
||||||
"sha.js@<=2.4.11": ">=2.4.12",
|
"sha.js@<=2.4.11": ">=2.4.12",
|
||||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
|
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
|
||||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
|
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
|
||||||
"tmp@<=0.2.3": ">=0.2.5",
|
"tmp@<=0.2.3": ">=0.2.5",
|
||||||
"vite@>=5.0.0 <=5.4.18": ">=7.2.7"
|
"vite@>=5.0.0 <=5.4.18": ">=7.1.12"
|
||||||
},
|
},
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
||||||
|
|||||||
@@ -22,17 +22,17 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/core": "workspace:^0.1.0",
|
|
||||||
"@homarr/db": "workspace:^0.1.0",
|
"@homarr/db": "workspace:^0.1.0",
|
||||||
|
"@homarr/log": "workspace:^0.1.0",
|
||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@umami/node": "^0.4.0",
|
"@umami/node": "^0.4.0",
|
||||||
"superjson": "2.2.6"
|
"superjson": "2.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.38.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
import type { UmamiEventData } from "@umami/node";
|
import type { UmamiEventData } from "@umami/node";
|
||||||
import { Umami } from "@umami/node";
|
import { Umami } from "@umami/node";
|
||||||
|
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { count, db } from "@homarr/db";
|
import { count, db } from "@homarr/db";
|
||||||
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
||||||
import { integrations, items, users } from "@homarr/db/schema";
|
import { integrations, items, users } from "@homarr/db/schema";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
import type { defaultServerSettings } from "@homarr/server-settings";
|
import type { defaultServerSettings } from "@homarr/server-settings";
|
||||||
|
|
||||||
import { Stopwatch } from "../../common/src";
|
import { Stopwatch } from "../../common/src";
|
||||||
import { UMAMI_HOST_URL, UMAMI_WEBSITE_ID } from "./constants";
|
import { UMAMI_HOST_URL, UMAMI_WEBSITE_ID } from "./constants";
|
||||||
|
|
||||||
const logger = createLogger({ module: "analytics" });
|
|
||||||
|
|
||||||
export const sendServerAnalyticsAsync = async () => {
|
export const sendServerAnalyticsAsync = async () => {
|
||||||
const stopWatch = new Stopwatch();
|
const stopWatch = new Stopwatch();
|
||||||
const analyticsSettings = await getServerSettingByKeyAsync(db, "analytics");
|
const analyticsSettings = await getServerSettingByKeyAsync(db, "analytics");
|
||||||
|
|||||||
+14
-12
@@ -22,6 +22,7 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/auth": "workspace:^0.1.0",
|
"@homarr/auth": "workspace:^0.1.0",
|
||||||
|
"@homarr/certificates": "workspace:^0.1.0",
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/core": "workspace:^0.1.0",
|
"@homarr/core": "workspace:^0.1.0",
|
||||||
"@homarr/cron-job-api": "workspace:^0.1.0",
|
"@homarr/cron-job-api": "workspace:^0.1.0",
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
"@homarr/docker": "workspace:^0.1.0",
|
"@homarr/docker": "workspace:^0.1.0",
|
||||||
"@homarr/icons": "workspace:^0.1.0",
|
"@homarr/icons": "workspace:^0.1.0",
|
||||||
"@homarr/integrations": "workspace:^0.1.0",
|
"@homarr/integrations": "workspace:^0.1.0",
|
||||||
|
"@homarr/log": "workspace:^",
|
||||||
"@homarr/old-import": "workspace:^0.1.0",
|
"@homarr/old-import": "workspace:^0.1.0",
|
||||||
"@homarr/old-schema": "workspace:^0.1.0",
|
"@homarr/old-schema": "workspace:^0.1.0",
|
||||||
"@homarr/ping": "workspace:^0.1.0",
|
"@homarr/ping": "workspace:^0.1.0",
|
||||||
@@ -41,25 +43,25 @@
|
|||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@kubernetes/client-node": "^1.4.0",
|
"@kubernetes/client-node": "^1.4.0",
|
||||||
"@tanstack/react-query": "^5.90.12",
|
"@tanstack/react-query": "^5.90.5",
|
||||||
"@trpc/client": "^11.8.0",
|
"@trpc/client": "^11.6.0",
|
||||||
"@trpc/react-query": "^11.8.0",
|
"@trpc/react-query": "^11.6.0",
|
||||||
"@trpc/server": "^11.8.0",
|
"@trpc/server": "^11.6.0",
|
||||||
"@trpc/tanstack-react-query": "^11.8.0",
|
"@trpc/tanstack-react-query": "^11.6.0",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"next": "16.0.10",
|
"next": "15.5.6",
|
||||||
"react": "19.2.3",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.0",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.3",
|
||||||
"trpc-to-openapi": "^3.1.0",
|
"trpc-to-openapi": "^3.1.0",
|
||||||
"zod": "^4.1.13"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.38.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import { createId } from "@homarr/common";
|
|||||||
import { asc, eq, inArray, like } from "@homarr/db";
|
import { asc, eq, inArray, like } from "@homarr/db";
|
||||||
import { apps } from "@homarr/db/schema";
|
import { apps } from "@homarr/db/schema";
|
||||||
import { selectAppSchema } from "@homarr/db/validationSchemas";
|
import { selectAppSchema } from "@homarr/db/validationSchemas";
|
||||||
import { getIconForNameAsync } from "@homarr/icons";
|
import { getIconForName } from "@homarr/icons";
|
||||||
import { appCreateManySchema, appEditSchema, appManageSchema } from "@homarr/validation/app";
|
import { appCreateManySchema, appEditSchema, appManageSchema } from "@homarr/validation/app";
|
||||||
import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
|
import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
|
||||||
|
|
||||||
@@ -141,15 +141,13 @@ export const appRouter = createTRPCRouter({
|
|||||||
.output(z.void())
|
.output(z.void())
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
await ctx.db.insert(apps).values(
|
await ctx.db.insert(apps).values(
|
||||||
await Promise.all(
|
input.map((app) => ({
|
||||||
input.map(async (app) => ({
|
id: createId(),
|
||||||
id: createId(),
|
name: app.name,
|
||||||
name: app.name,
|
description: app.description,
|
||||||
description: app.description,
|
iconUrl: app.iconUrl ?? getIconForName(ctx.db, app.name).sync()?.url ?? defaultIcon,
|
||||||
iconUrl: app.iconUrl ?? (await getIconForNameAsync(ctx.db, app.name))?.url ?? defaultIcon,
|
href: app.href,
|
||||||
href: app.href,
|
})),
|
||||||
})),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
update: permissionRequiredProcedure
|
update: permissionRequiredProcedure
|
||||||
|
|||||||
@@ -477,6 +477,47 @@ export const boardRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleSync(db) {
|
||||||
|
db.transaction((transaction) => {
|
||||||
|
transaction
|
||||||
|
.insert(boards)
|
||||||
|
.values({
|
||||||
|
...boardProps,
|
||||||
|
id: newBoardId,
|
||||||
|
name: input.name,
|
||||||
|
creatorId: ctx.session.user.id,
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
if (layoutsToInsert.length > 0) {
|
||||||
|
transaction.insert(layouts).values(layoutsToInsert).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionsToInsert.length > 0) {
|
||||||
|
transaction.insert(sections).values(sectionsToInsert).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionLayoutsToInsert.length > 0) {
|
||||||
|
transaction.insert(sectionLayouts).values(sectionLayoutsToInsert).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionCollapseStatesToInsert.length > 0) {
|
||||||
|
transaction.insert(sectionCollapseStates).values(sectionCollapseStatesToInsert).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsToInsert.length > 0) {
|
||||||
|
transaction.insert(items).values(itemsToInsert).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemLayoutsToInsert.length > 0) {
|
||||||
|
transaction.insert(itemLayouts).values(itemLayoutsToInsert).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIntegrationsToInsert.length > 0) {
|
||||||
|
transaction.insert(integrationItems).values(itemIntegrationsToInsert).run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
renameBoard: protectedProcedure.input(boardRenameSchema).mutation(async ({ ctx, input }) => {
|
renameBoard: protectedProcedure.input(boardRenameSchema).mutation(async ({ ctx, input }) => {
|
||||||
@@ -913,6 +954,216 @@ export const boardRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleSync(db) {
|
||||||
|
db.transaction((transaction) => {
|
||||||
|
const addedSections = filterAddedItems(input.sections, dbBoard.sections);
|
||||||
|
|
||||||
|
if (addedSections.length > 0) {
|
||||||
|
transaction
|
||||||
|
.insert(sections)
|
||||||
|
.values(
|
||||||
|
addedSections.map((section) => ({
|
||||||
|
id: section.id,
|
||||||
|
kind: section.kind,
|
||||||
|
yOffset: section.kind !== "dynamic" ? section.yOffset : null,
|
||||||
|
xOffset: section.kind === "dynamic" ? null : 0,
|
||||||
|
options: section.kind === "dynamic" ? superjson.stringify(section.options) : emptySuperJSON,
|
||||||
|
name: "name" in section ? section.name : null,
|
||||||
|
boardId: dbBoard.id,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
if (addedSections.some((section) => section.kind === "dynamic")) {
|
||||||
|
transaction
|
||||||
|
.insert(sectionLayouts)
|
||||||
|
.values(
|
||||||
|
addedSections
|
||||||
|
.filter((section) => section.kind === "dynamic")
|
||||||
|
.flatMap((section) =>
|
||||||
|
section.layouts.map(
|
||||||
|
(sectionLayout): InferInsertModel<typeof sectionLayouts> => ({
|
||||||
|
layoutId: sectionLayout.layoutId,
|
||||||
|
sectionId: section.id,
|
||||||
|
parentSectionId: sectionLayout.parentSectionId,
|
||||||
|
height: sectionLayout.height,
|
||||||
|
width: sectionLayout.width,
|
||||||
|
xOffset: sectionLayout.xOffset,
|
||||||
|
yOffset: sectionLayout.yOffset,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addedItems = filterAddedItems(input.items, dbBoard.items);
|
||||||
|
|
||||||
|
if (addedItems.length > 0) {
|
||||||
|
transaction
|
||||||
|
.insert(items)
|
||||||
|
.values(
|
||||||
|
addedItems.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
kind: item.kind,
|
||||||
|
options: superjson.stringify(item.options),
|
||||||
|
advancedOptions: superjson.stringify(item.advancedOptions),
|
||||||
|
boardId: dbBoard.id,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
transaction
|
||||||
|
.insert(itemLayouts)
|
||||||
|
.values(
|
||||||
|
addedItems.flatMap((item) =>
|
||||||
|
item.layouts.map(
|
||||||
|
(layoutSection): InferInsertModel<typeof itemLayouts> => ({
|
||||||
|
layoutId: layoutSection.layoutId,
|
||||||
|
sectionId: layoutSection.sectionId,
|
||||||
|
itemId: item.id,
|
||||||
|
height: layoutSection.height,
|
||||||
|
width: layoutSection.width,
|
||||||
|
xOffset: layoutSection.xOffset,
|
||||||
|
yOffset: layoutSection.yOffset,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputIntegrationRelations = input.items.flatMap(({ integrationIds, id: itemId }) =>
|
||||||
|
integrationIds.map((integrationId) => ({
|
||||||
|
integrationId,
|
||||||
|
itemId,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
const dbIntegrationRelations = dbBoard.items.flatMap(({ integrationIds, id: itemId }) =>
|
||||||
|
integrationIds.map((integrationId) => ({
|
||||||
|
integrationId,
|
||||||
|
itemId,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
const addedIntegrationRelations = inputIntegrationRelations.filter(
|
||||||
|
(inputRelation) =>
|
||||||
|
!dbIntegrationRelations.some(
|
||||||
|
(dbRelation) =>
|
||||||
|
dbRelation.itemId === inputRelation.itemId &&
|
||||||
|
dbRelation.integrationId === inputRelation.integrationId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (addedIntegrationRelations.length > 0) {
|
||||||
|
transaction
|
||||||
|
.insert(integrationItems)
|
||||||
|
.values(
|
||||||
|
addedIntegrationRelations.map((relation) => ({
|
||||||
|
itemId: relation.itemId,
|
||||||
|
integrationId: relation.integrationId,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedItems = filterUpdatedItems(input.items, dbBoard.items);
|
||||||
|
|
||||||
|
for (const item of updatedItems) {
|
||||||
|
transaction
|
||||||
|
.update(items)
|
||||||
|
.set({
|
||||||
|
kind: item.kind,
|
||||||
|
options: superjson.stringify(item.options),
|
||||||
|
advancedOptions: superjson.stringify(item.advancedOptions),
|
||||||
|
})
|
||||||
|
.where(eq(items.id, item.id))
|
||||||
|
.run();
|
||||||
|
|
||||||
|
for (const itemSectionLayout of item.layouts) {
|
||||||
|
transaction
|
||||||
|
.update(itemLayouts)
|
||||||
|
.set({
|
||||||
|
height: itemSectionLayout.height,
|
||||||
|
width: itemSectionLayout.width,
|
||||||
|
xOffset: itemSectionLayout.xOffset,
|
||||||
|
yOffset: itemSectionLayout.yOffset,
|
||||||
|
sectionId: itemSectionLayout.sectionId,
|
||||||
|
})
|
||||||
|
.where(and(eq(itemLayouts.itemId, item.id), eq(itemLayouts.layoutId, itemSectionLayout.layoutId)))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedSections = filterUpdatedItems(input.sections, dbBoard.sections);
|
||||||
|
|
||||||
|
for (const section of updatedSections) {
|
||||||
|
const prev = dbBoard.sections.find((dbSection) => dbSection.id === section.id);
|
||||||
|
transaction
|
||||||
|
.update(sections)
|
||||||
|
.set({
|
||||||
|
yOffset: prev?.kind !== "dynamic" && "yOffset" in section ? section.yOffset : null,
|
||||||
|
xOffset: prev?.kind !== "dynamic" && "yOffset" in section ? 0 : null,
|
||||||
|
options: section.kind === "dynamic" ? superjson.stringify(section.options) : emptySuperJSON,
|
||||||
|
name: prev?.kind === "category" && "name" in section ? section.name : null,
|
||||||
|
})
|
||||||
|
.where(eq(sections.id, section.id))
|
||||||
|
.run();
|
||||||
|
|
||||||
|
if (section.kind !== "dynamic") continue;
|
||||||
|
|
||||||
|
for (const sectionLayout of section.layouts) {
|
||||||
|
transaction
|
||||||
|
.update(sectionLayouts)
|
||||||
|
.set({
|
||||||
|
height: sectionLayout.height,
|
||||||
|
width: sectionLayout.width,
|
||||||
|
xOffset: sectionLayout.xOffset,
|
||||||
|
yOffset: sectionLayout.yOffset,
|
||||||
|
parentSectionId: sectionLayout.parentSectionId,
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
and(eq(sectionLayouts.sectionId, section.id), eq(sectionLayouts.layoutId, sectionLayout.layoutId)),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removedIntegrationRelations = dbIntegrationRelations.filter(
|
||||||
|
(dbRelation) =>
|
||||||
|
!inputIntegrationRelations.some(
|
||||||
|
(inputRelation) =>
|
||||||
|
dbRelation.itemId === inputRelation.itemId &&
|
||||||
|
dbRelation.integrationId === inputRelation.integrationId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const relation of removedIntegrationRelations) {
|
||||||
|
transaction
|
||||||
|
.delete(integrationItems)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(integrationItems.itemId, relation.itemId),
|
||||||
|
eq(integrationItems.integrationId, relation.integrationId),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
const removedItems = filterRemovedItems(input.items, dbBoard.items);
|
||||||
|
|
||||||
|
const itemIds = removedItems.map((item) => item.id);
|
||||||
|
if (itemIds.length > 0) {
|
||||||
|
transaction.delete(items).where(inArray(items.id, itemIds)).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
const removedSections = filterRemovedItems(input.sections, dbBoard.sections);
|
||||||
|
const sectionIds = removedSections.map((section) => section.id);
|
||||||
|
|
||||||
|
if (sectionIds.length > 0) {
|
||||||
|
transaction.delete(sections).where(inArray(sections.id, sectionIds)).run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
getBoardPermissions: protectedProcedure.input(byIdSchema).query(async ({ input, ctx }) => {
|
getBoardPermissions: protectedProcedure.input(byIdSchema).query(async ({ input, ctx }) => {
|
||||||
@@ -1005,6 +1256,24 @@ export const boardRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleSync(db) {
|
||||||
|
db.transaction((transaction) => {
|
||||||
|
transaction.delete(boardUserPermissions).where(eq(boardUserPermissions.boardId, input.entityId)).run();
|
||||||
|
if (input.permissions.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transaction
|
||||||
|
.insert(boardUserPermissions)
|
||||||
|
.values(
|
||||||
|
input.permissions.map((permission) => ({
|
||||||
|
userId: permission.principalId,
|
||||||
|
permission: permission.permission,
|
||||||
|
boardId: input.entityId,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
saveGroupBoardPermissions: protectedProcedure.input(boardSavePermissionsSchema).mutation(async ({ input, ctx }) => {
|
saveGroupBoardPermissions: protectedProcedure.input(boardSavePermissionsSchema).mutation(async ({ input, ctx }) => {
|
||||||
@@ -1028,6 +1297,24 @@ export const boardRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleSync(db) {
|
||||||
|
db.transaction((transaction) => {
|
||||||
|
transaction.delete(boardGroupPermissions).where(eq(boardGroupPermissions.boardId, input.entityId)).run();
|
||||||
|
if (input.permissions.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transaction
|
||||||
|
.insert(boardGroupPermissions)
|
||||||
|
.values(
|
||||||
|
input.permissions.map((permission) => ({
|
||||||
|
groupId: permission.principalId,
|
||||||
|
permission: permission.permission,
|
||||||
|
boardId: input.entityId,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
importOldmarrConfig: permissionRequiredProcedure
|
importOldmarrConfig: permissionRequiredProcedure
|
||||||
|
|||||||
@@ -3,19 +3,14 @@ import { TRPCError } from "@trpc/server";
|
|||||||
import { zfd } from "zod-form-data";
|
import { zfd } from "zod-form-data";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import {
|
import { addCustomRootCertificateAsync, removeCustomRootCertificateAsync } from "@homarr/certificates/server";
|
||||||
addCustomRootCertificateAsync,
|
|
||||||
removeCustomRootCertificateAsync,
|
|
||||||
} from "@homarr/core/infrastructure/certificates";
|
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
|
||||||
import { and, eq } from "@homarr/db";
|
import { and, eq } from "@homarr/db";
|
||||||
import { trustedCertificateHostnames } from "@homarr/db/schema";
|
import { trustedCertificateHostnames } from "@homarr/db/schema";
|
||||||
|
import { logger } from "@homarr/log";
|
||||||
import { certificateValidFileNameSchema, checkCertificateFile } from "@homarr/validation/certificates";
|
import { certificateValidFileNameSchema, checkCertificateFile } from "@homarr/validation/certificates";
|
||||||
|
|
||||||
import { createTRPCRouter, permissionRequiredProcedure } from "../../trpc";
|
import { createTRPCRouter, permissionRequiredProcedure } from "../../trpc";
|
||||||
|
|
||||||
const logger = createLogger({ module: "certificateRouter" });
|
|
||||||
|
|
||||||
export const certificateRouter = createTRPCRouter({
|
export const certificateRouter = createTRPCRouter({
|
||||||
addCertificate: permissionRequiredProcedure
|
addCertificate: permissionRequiredProcedure
|
||||||
.requiresPermission("admin")
|
.requiresPermission("admin")
|
||||||
@@ -91,7 +86,7 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
hostname: input.hostname,
|
hostname: input.hostname,
|
||||||
thumbprint: input.thumbprint,
|
thumbprint: input.thumbprint,
|
||||||
});
|
});
|
||||||
await ctx.db
|
const dbResult = await ctx.db
|
||||||
.delete(trustedCertificateHostnames)
|
.delete(trustedCertificateHostnames)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
@@ -103,6 +98,7 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
logger.info("Removed trusted hostname", {
|
logger.info("Removed trusted hostname", {
|
||||||
hostname: input.hostname,
|
hostname: input.hostname,
|
||||||
thumbprint: input.thumbprint,
|
thumbprint: input.thumbprint,
|
||||||
|
count: dbResult.changes,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
removeCertificate: permissionRequiredProcedure
|
removeCertificate: permissionRequiredProcedure
|
||||||
|
|||||||
Loaded 100 of 416 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user