Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe551b7cbe | ||
|
|
7ff38d54c8 |
No files matched your search
@@ -12,6 +12,10 @@ AUTH_SECRET="supersecret"
|
||||
# or starting the project without any (which will show a randomly generated one).
|
||||
SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
# Cron job API key is generated every time the container starts as it is required for communication between nextjs-api and tasks-api
|
||||
# export CRON_JOB_API_KEY=$(openssl rand -base64 32)
|
||||
# CRON_JOB_API_KEY="your-generated-api-key"
|
||||
|
||||
LOG_LEVEL='info'
|
||||
|
||||
# This is how you can use the sqlite driver:
|
||||
|
||||
@@ -33,9 +33,6 @@ body:
|
||||
options:
|
||||
# The below comment is used to insert a new version with on-release.yml
|
||||
#NEXT_VERSION#
|
||||
- 1.59.3
|
||||
- 1.59.2
|
||||
- 1.59.1
|
||||
- 1.59.0
|
||||
- 1.58.1
|
||||
- 1.58.0
|
||||
|
||||
@@ -25,16 +25,16 @@ body:
|
||||
label: Public API available?
|
||||
description: Is there a public API available, that we can consume in Homarr?
|
||||
options:
|
||||
- "Yes, available on a website"
|
||||
- "Yes, available in the application itself"
|
||||
- "No"
|
||||
- Yes, available on a website
|
||||
- Yes, available in the application itself
|
||||
- No
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Are you willing to contribute this yourself?
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
- Yes
|
||||
- No
|
||||
validations:
|
||||
required: true
|
||||
@@ -24,7 +24,7 @@ body:
|
||||
attributes:
|
||||
label: Are you willing to contribute this yourself?
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
- Yes
|
||||
- No
|
||||
validations:
|
||||
required: true
|
||||
@@ -11,12 +11,6 @@
|
||||
matchPackageNames: ["tsx"],
|
||||
enabled: false,
|
||||
},
|
||||
// Reenable once bug with undici v8 is fixed (https://github.com/homarr-labs/homarr/issues/5467)
|
||||
{
|
||||
matchPackageNames: ["undici"],
|
||||
matchNewValue: "8.*",
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
matchUpdateTypes: ["minor", "patch", "pin", "digest"],
|
||||
automerge: true,
|
||||
|
||||
@@ -9,12 +9,7 @@ jobs:
|
||||
approve-automatic-prs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
# 158783068: homarr-renovate[bot]
|
||||
# 190541745: homarr-crowdin[bot]
|
||||
# 210161987: homarr-update-contributors[bot]
|
||||
# 175486441: homarr-releases[bot]
|
||||
# Extracted from https://api.github.com/users/<username>
|
||||
if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 || github.actor_id == 175486441
|
||||
if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 # Id of renovate bot and crowdin bot see https://api.github.com/users/homarr-renovate%5Bbot%5D and https://api.github.com/users/homarr-crowdin%5Bbot%5D and https://api.github.com/users/homarr-update-contributors%5Bbot%5D
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
@@ -23,7 +18,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
permission-pull-requests: write # required to approve pull request
|
||||
|
||||
- name: Install GitHub CLI
|
||||
|
||||
@@ -11,10 +11,14 @@ on:
|
||||
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
# You can leverage Vercel Remote Caching with Turbo to speed up your builds
|
||||
# @link https://turborepo.org/docs/core-concepts/remote-caching#remote-caching-on-vercel-builds
|
||||
env:
|
||||
FORCE_COLOR: 3
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
@@ -55,7 +59,7 @@ jobs:
|
||||
uses: ./tooling/github/setup
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm typecheck
|
||||
run: turbo typecheck
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.CROWDIN_APP_ID }}
|
||||
app-id: ${{ secrets.CROWDIN_APP_ID }}
|
||||
permission-contents: write # required to commit to crowdin branch
|
||||
permission-pull-requests: write # required to create pull request
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
name: "[Deployment] Beta Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
send-notifications:
|
||||
type: boolean
|
||||
required: false
|
||||
default: true
|
||||
description: Send notifications
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
create-and-merge-pr:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Discord notification
|
||||
if: ${{ github.event.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "Beta release has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get Next Version
|
||||
id: semver
|
||||
uses: ietf-tools/semver-action@v1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
branch: next
|
||||
- name: Create pull request
|
||||
id: create-pull-request
|
||||
run: 'gh pr create --title "chore(release): release ${{ steps.semver.outputs.next }}-beta" --body "Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}-beta``" --base beta --head next --label automerge'
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Discord notification
|
||||
if: ${{ github.event.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }}-beta (new behaviour: ${{ steps.semver.outputs.bump }})"
|
||||
- name: Obtain token
|
||||
id: obtainApprovalToken
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
permission-pull-requests: write
|
||||
- name: Approve PR
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainApprovalToken.outputs.token }}
|
||||
run: |
|
||||
gh pr review --approve --body "Automatically approved by GitHub Action"
|
||||
- name: Obtain token
|
||||
id: obtainMergeToken
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
permission-contents: write # write to beta branch (due to merge)
|
||||
permission-pull-requests: write # merge pull request
|
||||
- id: automerge
|
||||
if: ${{ steps.semver.outputs.bump != 'major' }}
|
||||
name: automerge
|
||||
uses: "pascalgn/automerge-action@v0.16.4"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainMergeToken.outputs.token }}
|
||||
MERGE_METHOD: merge # we prefer merge commits for merging to beta
|
||||
MERGE_COMMIT_MESSAGE: "chore(release): release ${{ steps.semver.outputs.next }}-beta"
|
||||
MERGE_DELETE_BRANCH: false # never set to true!
|
||||
PULL_REQUEST: "${{ steps.create-pull-request.outputs.pr_number }}"
|
||||
MERGE_RETRIES: 20 # 20 retries * MERGE_RETRY_SLEEP until step fails
|
||||
MERGE_RETRY_SLEEP: 10000 # 10 seconds * MERGE_RETRIES until step fails
|
||||
MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
|
||||
|
||||
- name: Merged Discord notification
|
||||
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.event.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "Merged PR ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}-beta"
|
||||
- name: Major Bump Discord notification
|
||||
if: ${{ steps.semver.outputs.bump == 'major' && github.event.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "The release PR must be manually merged because the next version is a major version: ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}-beta"
|
||||
- name: Discord Fail Notification
|
||||
if: failure() && github.event.inputs.send-notifications
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "The beta release workflow [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) has failed"
|
||||
@@ -5,7 +5,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -35,7 +34,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' || github.ref_name == 'next' }}
|
||||
SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' }}
|
||||
outputs:
|
||||
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
|
||||
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
|
||||
@@ -54,20 +53,20 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
permission-contents: write # required to commit package.json & changelog changes, merge them to dev / next and publish the release
|
||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
permission-contents: write # required to commit package.json & changelog changes, merge them to dev and publish the release
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@v5
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
- uses: actions/setup-node@v6
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
with:
|
||||
node-version: 24.15.0
|
||||
package-manager-cache: false
|
||||
node-version: 24.14.1
|
||||
cache: "pnpm"
|
||||
- run: npm i -g pnpm
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
- name: Install dependencies
|
||||
@@ -96,21 +95,20 @@ jobs:
|
||||
id: read-git-ref
|
||||
run: |
|
||||
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
- name: Update dev / next branch
|
||||
- name: Update dev branch
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
continue-on-error: true # Prevent pipeline from failing when merge fails
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
UPSTREAM_BRANCH: ${{ github.ref_name == 'main' && 'dev' || 'next' }}
|
||||
run: |
|
||||
git config user.name "Releases Homarr"
|
||||
git config user.email "175486441+homarr-releases[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${{ steps.obtainToken.outputs.token }}@github.com/${{ github.repository }}.git
|
||||
git fetch origin ${{ env.UPSTREAM_BRANCH }}
|
||||
git checkout ${{ env.UPSTREAM_BRANCH }}
|
||||
git pull origin ${{ env.UPSTREAM_BRANCH }}
|
||||
git fetch origin dev
|
||||
git checkout dev
|
||||
git pull origin dev
|
||||
git rebase ${{ github.ref_name }}
|
||||
git push origin ${{ env.UPSTREAM_BRANCH }}
|
||||
git push origin dev
|
||||
build-amd64:
|
||||
name: Build docker image for amd64
|
||||
needs: release
|
||||
@@ -236,7 +234,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
permission-contents: write # required to publish the release
|
||||
|
||||
- name: Log in to the Container registry
|
||||
@@ -276,6 +274,6 @@ jobs:
|
||||
- name: Discord notification
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "Successfully deployed images for branch **${{ github.ref_name }}**. Tagged as **${{env.NEXT_VERSION}}**."
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Discord notification
|
||||
if: ${{ github.event.inputs.send-notifications }}
|
||||
if: ${{ github.events.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "Automatic release has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||
- name: Checkout repository
|
||||
@@ -38,15 +38,14 @@ jobs:
|
||||
token: ${{ github.token }}
|
||||
branch: dev
|
||||
- name: Create pull request
|
||||
id: create-pull-request
|
||||
run: 'gh pr create --title "chore(release): automatic release ${{ steps.semver.outputs.next }}" --body "**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``" --base main --head dev --label automerge'
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Discord notification
|
||||
if: ${{ github.event.inputs.send-notifications }}
|
||||
if: ${{ github.events.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
|
||||
- name: Obtain token
|
||||
@@ -54,7 +53,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
permission-pull-requests: write
|
||||
- name: Approve PR
|
||||
env:
|
||||
@@ -66,7 +65,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
permission-contents: write # write to main branch (due to merge)
|
||||
permission-pull-requests: write # merge pull request
|
||||
- id: automerge
|
||||
@@ -75,7 +74,7 @@ jobs:
|
||||
uses: "pascalgn/automerge-action@v0.16.4"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainMergeToken.outputs.token }}
|
||||
MERGE_METHOD: merge # we prefer merge commits for merging to main
|
||||
MERGE_METHOD: merge # we prefer merge commits for merging to master
|
||||
MERGE_COMMIT_MESSAGE: "chore(release): automatic release ${{ steps.semver.outputs.next }}"
|
||||
MERGE_DELETE_BRANCH: false # never set to true!
|
||||
PULL_REQUEST: "${{ steps.create-pull-request.outputs.pr_number }}"
|
||||
@@ -84,23 +83,23 @@ jobs:
|
||||
MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
|
||||
|
||||
- name: Merged Discord notification
|
||||
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.event.inputs.send-notifications }}
|
||||
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.events.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "Merged PR ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
|
||||
- name: Major Bump Discord notification
|
||||
if: ${{ steps.semver.outputs.bump == 'major' && github.event.inputs.send-notifications }}
|
||||
if: ${{ steps.semver.outputs.bump == 'major' && github.events.inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "The release PR must be manually merged because the next version is a major version: ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
|
||||
- name: Discord Fail Notification
|
||||
if: failure() && github.event.inputs.send-notifications
|
||||
if: failure() && github.events.inputs.send-notifications
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "The automatic release workflow [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) has failed"
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
|
||||
client-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
|
||||
app-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
|
||||
owner: homarr-labs
|
||||
repositories: |
|
||||
documentation
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
permission-contents: write # required to commit to branch
|
||||
permission-pull-requests: write # required to create pr & enable automerge
|
||||
- name: Checkout code
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
||||
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||
permission-contents: write # required to commit to branch
|
||||
permission-pull-requests: write # required to create pr & enable automerge
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@v5
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
||||
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||
permission-contents: write # required to commit to branch
|
||||
permission-pull-requests: write # required to create pr & enable automerge
|
||||
- name: Checkout code
|
||||
|
||||
@@ -1,94 +1,3 @@
|
||||
## [1.60.1-beta.1](https://github.com/homarr-labs/homarr/compare/v1.60.0...v1.60.1-beta.1) (2026-05-01)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** combine 3 node processes into one ([#5600](https://github.com/homarr-labs/homarr/issues/5600)) ([a1aa550](https://github.com/homarr-labs/homarr/commit/a1aa5505a365688b456090a882770e561479833e))
|
||||
|
||||
## [1.60.1-beta.1](https://github.com/homarr-labs/homarr/compare/v1.60.0...v1.60.1-beta.1) (2026-05-01)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** combine 3 node processes into one ([#5600](https://github.com/homarr-labs/homarr/issues/5600)) ([a1aa550](https://github.com/homarr-labs/homarr/commit/a1aa5505a365688b456090a882770e561479833e))
|
||||
|
||||
## [1.60.1-beta.1](https://github.com/homarr-labs/homarr/compare/v1.60.0...v1.60.1-beta.1) (2026-05-01)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** combine 3 node processes into one ([#5600](https://github.com/homarr-labs/homarr/issues/5600)) ([a1aa550](https://github.com/homarr-labs/homarr/commit/a1aa5505a365688b456090a882770e561479833e))
|
||||
|
||||
## [1.60.0](https://github.com/homarr-labs/homarr/compare/v1.59.3...v1.60.0) (2026-05-01)
|
||||
|
||||
### Features
|
||||
|
||||
* **about:** add opencollective contributors ([#5481](https://github.com/homarr-labs/homarr/issues/5481)) ([3d202d4](https://github.com/homarr-labs/homarr/commit/3d202d409febf33d0218b589c979744c84f87407))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* migrate from node-cron to croner ([#5407](https://github.com/homarr-labs/homarr/issues/5407)) ([e8edc20](https://github.com/homarr-labs/homarr/commit/e8edc203b6c862141668b043ef4232c22b8fae24))
|
||||
* **openapi:** openapi document empty ([#5579](https://github.com/homarr-labs/homarr/issues/5579)) ([ce8e330](https://github.com/homarr-labs/homarr/commit/ce8e330c82bc0be9f299f3071a1e80daafc5f789))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **icons:** updater takes 10 seconds to run with high cpu usage ([#5608](https://github.com/homarr-labs/homarr/issues/5608)) ([ed6680b](https://github.com/homarr-labs/homarr/commit/ed6680ba6a42864185818eb8d90b2303846c1761))
|
||||
|
||||
## [1.60.0](https://github.com/homarr-labs/homarr/compare/v1.59.3...v1.60.0) (2026-05-01)
|
||||
|
||||
### Features
|
||||
|
||||
* **about:** add opencollective contributors ([#5481](https://github.com/homarr-labs/homarr/issues/5481)) ([3d202d4](https://github.com/homarr-labs/homarr/commit/3d202d409febf33d0218b589c979744c84f87407))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* migrate from node-cron to croner ([#5407](https://github.com/homarr-labs/homarr/issues/5407)) ([e8edc20](https://github.com/homarr-labs/homarr/commit/e8edc203b6c862141668b043ef4232c22b8fae24))
|
||||
* **openapi:** openapi document empty ([#5579](https://github.com/homarr-labs/homarr/issues/5579)) ([ce8e330](https://github.com/homarr-labs/homarr/commit/ce8e330c82bc0be9f299f3071a1e80daafc5f789))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **icons:** updater takes 10 seconds to run with high cpu usage ([#5608](https://github.com/homarr-labs/homarr/issues/5608)) ([ed6680b](https://github.com/homarr-labs/homarr/commit/ed6680ba6a42864185818eb8d90b2303846c1761))
|
||||
|
||||
## [1.59.3](https://github.com/homarr-labs/homarr/compare/v1.59.2...v1.59.3) (2026-04-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency better-sqlite3 to ^12.9.0 ([#5531](https://github.com/homarr-labs/homarr/issues/5531)) ([7852f1d](https://github.com/homarr-labs/homarr/commit/7852f1d987d40707ae13494f9c934a563581f97f))
|
||||
|
||||
## [1.59.3](https://github.com/homarr-labs/homarr/compare/v1.59.2...v1.59.3) (2026-04-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency better-sqlite3 to ^12.9.0 ([#5531](https://github.com/homarr-labs/homarr/issues/5531)) ([7852f1d](https://github.com/homarr-labs/homarr/commit/7852f1d987d40707ae13494f9c934a563581f97f))
|
||||
|
||||
## [1.59.2](https://github.com/homarr-labs/homarr/compare/v1.59.1...v1.59.2) (2026-04-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **about:** add missing dependencies ([#5480](https://github.com/homarr-labs/homarr/issues/5480)) ([c48588c](https://github.com/homarr-labs/homarr/commit/c48588c6a748920b05f8afcd79fad17dc65f2930))
|
||||
* **deps:** update dependency @drizzle-team/brocli to ^0.12.0 ([#5489](https://github.com/homarr-labs/homarr/issues/5489)) ([74611b7](https://github.com/homarr-labs/homarr/commit/74611b75a736df43a77ebc780d2e0eee5c40e3f3))
|
||||
* **deps:** update dependency @immich/sdk to ^2.7.0 ([#5490](https://github.com/homarr-labs/homarr/issues/5490)) ([9cdf2aa](https://github.com/homarr-labs/homarr/commit/9cdf2aa1f2b1477522879095b603b100c1dd3ac0))
|
||||
* **deps:** update dependency @immich/sdk to ^2.7.2 ([#5494](https://github.com/homarr-labs/homarr/issues/5494)) ([0927f45](https://github.com/homarr-labs/homarr/commit/0927f45b2573753152fd5e9e318ba62b88abca35))
|
||||
* **deps:** update dependency fast-xml-parser to ^5.5.11 ([#5500](https://github.com/homarr-labs/homarr/issues/5500)) ([652ed1a](https://github.com/homarr-labs/homarr/commit/652ed1a5ed0beeb2dbd44976890f24dc12b90cf6))
|
||||
* **deps:** update dependency isomorphic-dompurify to ^3.8.0 ([#5503](https://github.com/homarr-labs/homarr/issues/5503)) ([0731b7c](https://github.com/homarr-labs/homarr/commit/0731b7c5ef272bcf69c566cdec723f2696309e67))
|
||||
* **deps:** update dependency jotai to ^2.19.1 ([#5487](https://github.com/homarr-labs/homarr/issues/5487)) ([efd8a55](https://github.com/homarr-labs/homarr/commit/efd8a55b6ea113bc42bc90d9ccb0db6c1d2b68ef))
|
||||
* **deps:** update dependency next-intl to v4.9.1 [security] ([#5470](https://github.com/homarr-labs/homarr/issues/5470)) ([f2437c4](https://github.com/homarr-labs/homarr/commit/f2437c473106689e9fa93e7dcbea84b00800ea4a))
|
||||
* **deps:** update dependency swagger-ui-react to ^5.32.2 ([#5488](https://github.com/homarr-labs/homarr/issues/5488)) ([cf131c5](https://github.com/homarr-labs/homarr/commit/cf131c5c51f6c21f942ab89d02353f93dc05774c))
|
||||
* **deps:** update dependency typescript-eslint to ^8.58.1 ([#5496](https://github.com/homarr-labs/homarr/issues/5496)) ([f88984c](https://github.com/homarr-labs/homarr/commit/f88984cda844beb1099a08c9cdeb6691ad13b5a8))
|
||||
* **deps:** update react monorepo to v19.2.5 ([#5502](https://github.com/homarr-labs/homarr/issues/5502)) ([c8a61a9](https://github.com/homarr-labs/homarr/commit/c8a61a9df3087de28c76f573623725eec246dd32))
|
||||
* **deps:** update testcontainers-node monorepo to ^11.14.0 ([#5498](https://github.com/homarr-labs/homarr/issues/5498)) ([1057b47](https://github.com/homarr-labs/homarr/commit/1057b478c37d02e9de7bcd155f0b3fb764149abe))
|
||||
* **deps:** update tiptap monorepo to v3.22.3 ([#5499](https://github.com/homarr-labs/homarr/issues/5499)) ([95abd2c](https://github.com/homarr-labs/homarr/commit/95abd2c9d3a563c7eaebe12f3b8015e3299e27e8))
|
||||
* **deps:** update turbo monorepo to ^2.9.4 ([#5476](https://github.com/homarr-labs/homarr/issues/5476)) ([813f2fa](https://github.com/homarr-labs/homarr/commit/813f2fa5efb999d3364882578fdf456537dba3a6))
|
||||
* **deps:** update turbo monorepo to ^2.9.5 ([#5491](https://github.com/homarr-labs/homarr/issues/5491)) ([ed24ce6](https://github.com/homarr-labs/homarr/commit/ed24ce6381466fa530ce225363231303be4ed92e))
|
||||
* preserve all-day event dates in Nextcloud calendar integration ([#5523](https://github.com/homarr-labs/homarr/issues/5523)) ([2e78979](https://github.com/homarr-labs/homarr/commit/2e789795ed11c816550b6a3ce88fe92a360ede9c))
|
||||
|
||||
## [1.59.1](https://github.com/homarr-labs/homarr/compare/v1.59.0...v1.59.1) (2026-04-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **qbittorrent:** requests fail ([#5473](https://github.com/homarr-labs/homarr/issues/5473)) ([db6bb87](https://github.com/homarr-labs/homarr/commit/db6bb870e3be14c7895a2882755d3aee09e44fa6))
|
||||
|
||||
### Reverts
|
||||
|
||||
* **deps:** downgrade undici from v8 to v7 ([#5473](https://github.com/homarr-labs/homarr/issues/5473)) ([dc44fac](https://github.com/homarr-labs/homarr/commit/dc44fac42aab5679427b2921402dfa11e634e1cf))
|
||||
|
||||
## [1.59.0](https://github.com/homarr-labs/homarr/compare/v1.58.1...v1.59.0) (2026-04-10)
|
||||
|
||||
### Features
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:24.15.0-alpine AS base
|
||||
FROM node:24.14.1-alpine AS base
|
||||
|
||||
FROM base AS builder
|
||||
RUN apk add --no-cache libc6-compat
|
||||
@@ -43,6 +43,8 @@ RUN mkdir -p /var/cache/nginx && \
|
||||
COPY --from=builder /app/apps/nextjs/next.config.ts .
|
||||
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/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
|
||||
|
||||
@@ -32,8 +32,6 @@ const nextConfig: NextConfig = {
|
||||
experimental: {
|
||||
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
|
||||
turbopackFileSystemCacheForDev: true,
|
||||
preloadEntriesOnStart: false,
|
||||
webpackMemoryOptimizations: true,
|
||||
},
|
||||
transpilePackages: ["@homarr/ui", "@homarr/notifications", "@homarr/modals", "@homarr/spotlight", "@homarr/widgets"],
|
||||
images: {
|
||||
|
||||
+56
-59
@@ -15,10 +15,10 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "catalog:",
|
||||
"@dnd-kit/modifiers": "catalog:",
|
||||
"@dnd-kit/sortable": "catalog:",
|
||||
"@dnd-kit/utilities": "catalog:",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@homarr/analytics": "workspace:^0.1.0",
|
||||
"@homarr/api": "workspace:^0.1.0",
|
||||
"@homarr/auth": "workspace:^0.1.0",
|
||||
@@ -31,7 +31,7 @@
|
||||
"@homarr/docker": "workspace:^0.1.0",
|
||||
"@homarr/form": "workspace:^0.1.0",
|
||||
"@homarr/forms-collection": "workspace:^0.1.0",
|
||||
"@homarr/gridstack": "catalog:",
|
||||
"@homarr/gridstack": "^1.12.0",
|
||||
"@homarr/icons": "workspace:^0.1.0",
|
||||
"@homarr/image-proxy": "workspace:^0.1.0",
|
||||
"@homarr/integrations": "workspace:^0.1.0",
|
||||
@@ -44,68 +44,65 @@
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@homarr/settings": "workspace:^0.1.0",
|
||||
"@homarr/spotlight": "workspace:^0.1.0",
|
||||
"@homarr/tasks": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@homarr/websocket": "workspace:^0.1.0",
|
||||
"@homarr/widgets": "workspace:^0.1.0",
|
||||
"@mantine/colors-generator": "catalog:",
|
||||
"@mantine/core": "catalog:",
|
||||
"@mantine/dropzone": "catalog:",
|
||||
"@mantine/hooks": "catalog:",
|
||||
"@mantine/modals": "catalog:",
|
||||
"@mantine/tiptap": "catalog:",
|
||||
"@million/lint": "catalog:",
|
||||
"@tabler/icons-react": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/react-query-devtools": "catalog:",
|
||||
"@tanstack/react-query-next-experimental": "catalog:",
|
||||
"@trpc/client": "catalog:",
|
||||
"@trpc/next": "catalog:",
|
||||
"@trpc/react-query": "catalog:",
|
||||
"@trpc/server": "catalog:",
|
||||
"@xterm/addon-canvas": "catalog:",
|
||||
"@xterm/addon-fit": "catalog:",
|
||||
"@xterm/xterm": "catalog:",
|
||||
"babel-plugin-react-compiler": "catalog:",
|
||||
"chroma-js": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"dotenv": "catalog:",
|
||||
"flag-icons": "catalog:",
|
||||
"glob": "catalog:",
|
||||
"isomorphic-dompurify": "catalog:",
|
||||
"jotai": "catalog:",
|
||||
"mantine-react-table": "catalog:",
|
||||
"next": "catalog:",
|
||||
"postcss-preset-mantine": "catalog:",
|
||||
"prismjs": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-error-boundary": "catalog:",
|
||||
"react-simple-code-editor": "catalog:",
|
||||
"sass": "catalog:",
|
||||
"superjson": "catalog:",
|
||||
"swagger-ui-react": "catalog:",
|
||||
"use-deep-compare-effect": "catalog:",
|
||||
"yaml": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@mantine/colors-generator": "^8.3.18",
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@mantine/dropzone": "^8.3.18",
|
||||
"@mantine/hooks": "^8.3.18",
|
||||
"@mantine/modals": "^8.3.18",
|
||||
"@mantine/tiptap": "^8.3.18",
|
||||
"@million/lint": "1.0.14",
|
||||
"@tabler/icons-react": "^3.41.1",
|
||||
"@tanstack/react-query": "^5.96.2",
|
||||
"@tanstack/react-query-devtools": "^5.96.2",
|
||||
"@tanstack/react-query-next-experimental": "^5.96.2",
|
||||
"@trpc/client": "^11.16.0",
|
||||
"@trpc/next": "^11.16.0",
|
||||
"@trpc/react-query": "^11.16.0",
|
||||
"@trpc/server": "^11.16.0",
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-fit": "0.11.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"babel-plugin-react-compiler": "^1.0.0",
|
||||
"chroma-js": "^3.2.0",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.20",
|
||||
"dotenv": "^17.4.1",
|
||||
"flag-icons": "^7.5.0",
|
||||
"glob": "^13.0.6",
|
||||
"isomorphic-dompurify": "^3.7.1",
|
||||
"jotai": "^2.19.0",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "16.2.3",
|
||||
"postcss-preset-mantine": "^1.18.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"react-error-boundary": "^6.1.1",
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"sass": "^1.99.0",
|
||||
"superjson": "2.2.6",
|
||||
"swagger-ui-react": "^5.32.1",
|
||||
"use-deep-compare-effect": "^1.8.1",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/chroma-js": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/prismjs": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/swagger-ui-react": "catalog:",
|
||||
"concurrently": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"node-loader": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/chroma-js": "3.1.2",
|
||||
"@types/node": "^24.12.2",
|
||||
"@types/prismjs": "^1.26.6",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/swagger-ui-react": "^5.18.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.39.4",
|
||||
"node-loader": "^2.1.0",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
.contributorCard {
|
||||
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||
}
|
||||
|
||||
.dependencyCard:hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||
}
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
Flex,
|
||||
Group,
|
||||
Kbd,
|
||||
SimpleGrid,
|
||||
List,
|
||||
ListItem,
|
||||
Stack,
|
||||
Table,
|
||||
TableTbody,
|
||||
@@ -21,18 +22,9 @@ import {
|
||||
TableThead,
|
||||
TableTr,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconKeyboard,
|
||||
IconLanguage,
|
||||
IconLibrary,
|
||||
IconMoneybagHeart,
|
||||
IconPackage,
|
||||
IconUsers,
|
||||
} from "@tabler/icons-react";
|
||||
import { IconKeyboard, IconLanguage, IconLibrary, IconUsers } from "@tabler/icons-react";
|
||||
|
||||
import { capitalize, objectEntries } from "@homarr/common";
|
||||
import { hotkeys } from "@homarr/definitions";
|
||||
@@ -41,10 +33,8 @@ import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
import { createMetaTitle } from "~/metadata";
|
||||
import type { PackageJsonDependencies } from "~/versions/package-reader";
|
||||
import { getPackageVersion } from "~/versions/package-reader";
|
||||
import { getPackageAttributesAsync } from "~/versions/package-reader";
|
||||
import type githubContributorsJson from "../../../../../../../static-data/contributors.json";
|
||||
import type openCollectiveContributorsJson from "../../../../../../../static-data/opencollective-contributors.json";
|
||||
import type crowdinContributorsJson from "../../../../../../../static-data/translators.json";
|
||||
import classes from "./about.module.css";
|
||||
|
||||
@@ -67,10 +57,7 @@ const getHostAsync = async () => {
|
||||
export default async function AboutPage() {
|
||||
const baseServerUrl = `http://${await getHostAsync()}`;
|
||||
const t = await getScopedI18n("management.page.about");
|
||||
const version = getPackageVersion();
|
||||
const dependencies = (await fetch(`${baseServerUrl}/api/about/dependencies`).then((res) =>
|
||||
res.json(),
|
||||
)) as PackageJsonDependencies;
|
||||
const attributes = await getPackageAttributesAsync();
|
||||
const githubContributors = (await fetch(`${baseServerUrl}/api/about/contributors/github`).then((res) =>
|
||||
res.json(),
|
||||
)) as typeof githubContributorsJson;
|
||||
@@ -79,10 +66,6 @@ export default async function AboutPage() {
|
||||
res.json(),
|
||||
)) as typeof crowdinContributorsJson;
|
||||
|
||||
const openCollectiveContributors = (await fetch(`${baseServerUrl}/api/about/contributors/opencollective`).then(
|
||||
(res) => res.json(),
|
||||
)) as typeof openCollectiveContributorsJson;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DynamicBreadcrumb />
|
||||
@@ -93,7 +76,7 @@ export default async function AboutPage() {
|
||||
<Title order={1} tt="uppercase">
|
||||
Homarr
|
||||
</Title>
|
||||
<Title order={2}>{t("version", { version })}</Title>
|
||||
<Title order={2}>{t("version", { version: attributes.version })}</Title>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Center>
|
||||
@@ -148,72 +131,31 @@ export default async function AboutPage() {
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="sponsors">
|
||||
<AccordionControl icon={<IconMoneybagHeart size="1rem" />}>
|
||||
<Stack gap={0}>
|
||||
<Text>{t("accordion.sponsors.title")}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t("accordion.sponsors.subtitle", {
|
||||
count: String(openCollectiveContributors.length),
|
||||
})}
|
||||
</Text>
|
||||
</Stack>
|
||||
</AccordionControl>
|
||||
<AccordionPanel>
|
||||
<Flex wrap="wrap" gap="xs">
|
||||
{openCollectiveContributors.map((sponsor) => (
|
||||
<GenericContributorLinkCard
|
||||
key={sponsor.slug}
|
||||
link={`https://opencollective.com/${sponsor.slug}`}
|
||||
image={sponsor.imageUrl}
|
||||
name={sponsor.name}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="libraries">
|
||||
<AccordionControl icon={<IconLibrary size="1rem" />}>
|
||||
<Stack gap={0}>
|
||||
<Text>{t("accordion.libraries.title")}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t("accordion.libraries.subtitle", {
|
||||
count: String(Object.keys(dependencies).length),
|
||||
count: String(Object.keys(attributes.dependencies).length),
|
||||
})}
|
||||
</Text>
|
||||
</Stack>
|
||||
</AccordionControl>
|
||||
<AccordionPanel>
|
||||
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }} spacing="md">
|
||||
{Object.entries(dependencies)
|
||||
.filter(([, value]) => !value.includes("workspace:"))
|
||||
<List>
|
||||
{Object.entries(attributes.dependencies)
|
||||
.sort(([key1], [key2]) => key1.localeCompare(key2))
|
||||
.map(([name, version]) => (
|
||||
<UnstyledButton
|
||||
key={name}
|
||||
component="a"
|
||||
href={`https://www.npmjs.com/package/${name}`}
|
||||
target="_blank"
|
||||
>
|
||||
<Card withBorder radius="md" p="xs" className={classes.dependencyCard}>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ThemeIcon variant="light" size="lg" radius="md">
|
||||
<IconPackage size="1.5rem" stroke={1.5} />
|
||||
</ThemeIcon>
|
||||
|
||||
<Stack gap={0}>
|
||||
<Text size="sm" fw="bold" lineClamp={1} title={name}>
|
||||
{name}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
v{version.replace("^", "").replace("~", "")}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Card>
|
||||
</UnstyledButton>
|
||||
.map(([key, value]) => (
|
||||
<ListItem key={key}>
|
||||
{value.includes("workspace:") ? (
|
||||
<Text>{key}</Text>
|
||||
) : (
|
||||
<a href={`https://www.npmjs.com/package/${key}`}>{key}</a>
|
||||
)}
|
||||
</ListItem>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</List>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="hotkeys">
|
||||
|
||||
@@ -2,7 +2,7 @@ import { headers } from "next/headers";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from "@mantine/core";
|
||||
|
||||
import { openApiDocument } from "@homarr/api/open-api";
|
||||
import { openApiDocument } from "@homarr/api";
|
||||
import { api } from "@homarr/api/server";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import { extractBaseUrlFromHeaders } from "@homarr/common";
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
|
||||
},
|
||||
onSuccess(_, variables) {
|
||||
form.setInitialValues({
|
||||
firstDayOfWeek: variables.firstDayOfWeek as DayOfWeek,
|
||||
firstDayOfWeek: variables.firstDayOfWeek,
|
||||
});
|
||||
showSuccessNotification({
|
||||
message: t("user.action.changeFirstDayOfWeek.notification.success.message"),
|
||||
|
||||
@@ -2,8 +2,7 @@ import type { NextRequest } from "next/server";
|
||||
import { userAgent } from "next/server";
|
||||
import { createOpenApiFetchHandler } from "trpc-to-openapi";
|
||||
|
||||
import { createTRPCContext } from "@homarr/api";
|
||||
import { openApiRouter } from "@homarr/api/open-api";
|
||||
import { appRouter, createTRPCContext } from "@homarr/api";
|
||||
import { API_KEY_HEADER_NAME, getSessionFromApiKeyAsync } from "@homarr/auth/api-key";
|
||||
import { ipAddressFromHeaders } from "@homarr/common/server";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
@@ -31,7 +30,7 @@ const handlerAsync = async (req: NextRequest) => {
|
||||
return createOpenApiFetchHandler({
|
||||
req,
|
||||
endpoint: "/",
|
||||
router: openApiRouter,
|
||||
router: appRouter,
|
||||
createContext: () => createTRPCContext({ session, headers: req.headers }),
|
||||
onError({ error, path, type }) {
|
||||
logger.error(new ErrorWithMetadata("tRPC Error occured", { path, type }, { cause: error }));
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import openCollectiveContributors from "../../../../../../../../static-data/opencollective-contributors.json";
|
||||
|
||||
export const GET = () => {
|
||||
return NextResponse.json(openCollectiveContributors);
|
||||
};
|
||||
|
||||
export const dynamic = "force-static";
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { getDependenciesAsync } from "../../../../versions/package-reader";
|
||||
|
||||
export const GET = async () => {
|
||||
const dependencies = await getDependenciesAsync();
|
||||
return NextResponse.json(dependencies);
|
||||
};
|
||||
|
||||
export const dynamic = "force-static";
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { openApiDocument } from "@homarr/api/open-api";
|
||||
import { openApiDocument } from "@homarr/api";
|
||||
import { extractBaseUrlFromHeaders } from "@homarr/common";
|
||||
|
||||
export function GET(request: Request) {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||
await import("@homarr/tasks");
|
||||
await import("@homarr/websocket");
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
import fsPromises from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { glob } from "glob";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
import z from "zod";
|
||||
|
||||
import packageJson from "../../../../package.json";
|
||||
|
||||
export const getPackageVersion = () => packageJson.version;
|
||||
export const getDependenciesAsync = async (): Promise<PackageJsonDependencies> => {
|
||||
const getPackageVersion = () => packageJson.version;
|
||||
const getDependenciesAsync = async (): Promise<PackageJsonDependencies> => {
|
||||
const pathNames = await glob("**/package.json", {
|
||||
ignore: "**/node_modules/**",
|
||||
ignore: "node_modules/**",
|
||||
cwd: "../../",
|
||||
absolute: true,
|
||||
});
|
||||
@@ -19,37 +15,21 @@ export const getDependenciesAsync = async (): Promise<PackageJsonDependencies> =
|
||||
.map((packageContent) => (JSON.parse(packageContent) as PackageJson).dependencies)
|
||||
.filter((dependencies) => dependencies !== undefined);
|
||||
|
||||
const catalog = await parseDependencyCatalogsAsync();
|
||||
|
||||
let dependencies = {};
|
||||
for (const dependenciesOfPackage of packageDependencies) {
|
||||
const resolvedDependencies = Object.entries(dependenciesOfPackage).map(([name, version]) => {
|
||||
const catalogVersion = catalog.get(name);
|
||||
return [name, catalogVersion ?? version] as const;
|
||||
});
|
||||
dependencies = { ...dependencies, ...Object.fromEntries(resolvedDependencies) };
|
||||
dependencies = { ...dependencies, ...dependenciesOfPackage };
|
||||
}
|
||||
return dependencies;
|
||||
};
|
||||
|
||||
type DependencyCatalog = Map<string, string>;
|
||||
const parseDependencyCatalogsAsync = async (): Promise<DependencyCatalog> => {
|
||||
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const workspaceConfigContent = await fsPromises.readFile(
|
||||
path.join(currentDir, "..", "..", "..", "..", "pnpm-workspace.yaml"),
|
||||
"utf-8",
|
||||
);
|
||||
const workspaceConfig: unknown = parseYaml(workspaceConfigContent);
|
||||
const parseResult = workspaceConfigCatalogSchema.parse(workspaceConfig);
|
||||
|
||||
return new Map(Object.entries(parseResult.catalog ?? {}));
|
||||
export const getPackageAttributesAsync = async () => {
|
||||
return {
|
||||
version: getPackageVersion(),
|
||||
dependencies: await getDependenciesAsync(),
|
||||
};
|
||||
};
|
||||
|
||||
const workspaceConfigCatalogSchema = z.object({
|
||||
catalog: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
|
||||
export type PackageJsonDependencies = Record<string, string>;
|
||||
type PackageJsonDependencies = Record<string, string>;
|
||||
interface PackageJson {
|
||||
dependencies: PackageJsonDependencies | undefined;
|
||||
}
|
||||
+15
-14
@@ -5,11 +5,12 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/main.ts"
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"main": "./src/main.ts",
|
||||
"types": "./src/main.ts",
|
||||
"scripts": {
|
||||
"build": "esbuild src/main.ts --bundle --platform=node --loader:.scss=text --external:*.node --external:@opentelemetry/api --external:deasync --external:bcrypt --outfile=tasks.cjs",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"dev": "pnpm with-env tsx ./src/main.ts",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
@@ -23,6 +24,7 @@
|
||||
"@homarr/auth": "workspace:^0.1.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^",
|
||||
"@homarr/cron-job-api": "workspace:^0.1.0",
|
||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||
"@homarr/cron-jobs-core": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
@@ -35,23 +37,22 @@
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@homarr/widgets": "workspace:^0.1.0",
|
||||
"croner": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"dotenv": "catalog:",
|
||||
"fastify": "catalog:",
|
||||
"superjson": "catalog:",
|
||||
"undici": "catalog:"
|
||||
"dayjs": "^1.11.20",
|
||||
"dotenv": "^17.4.1",
|
||||
"fastify": "^5.8.4",
|
||||
"superjson": "2.2.6",
|
||||
"undici": "8.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node": "catalog:",
|
||||
"dotenv-cli": "catalog:",
|
||||
"esbuild": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/node": "^24.12.2",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.4",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Cron } from "croner";
|
||||
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 { jobGroup as cronJobGroup, JobGroupKeys } from "@homarr/cron-jobs";
|
||||
import type { Database, InferInsertModel } from "@homarr/db";
|
||||
import { eq } from "@homarr/db";
|
||||
@@ -8,7 +9,7 @@ import { cronJobConfigurations } from "@homarr/db/schema";
|
||||
|
||||
const logger = createLogger({ module: "jobManager" });
|
||||
|
||||
export class JobManager {
|
||||
export class JobManager implements IJobManager {
|
||||
constructor(
|
||||
private db: Database,
|
||||
private jobGroup: typeof cronJobGroup,
|
||||
@@ -20,31 +21,28 @@ export class JobManager {
|
||||
public async triggerAsync(name: JobGroupKeys): Promise<void> {
|
||||
await this.jobGroup.runManuallyAsync(name);
|
||||
}
|
||||
public stop(name: JobGroupKeys): void {
|
||||
this.jobGroup.stop(name);
|
||||
public async stopAsync(name: JobGroupKeys): Promise<void> {
|
||||
await this.jobGroup.stopAsync(name);
|
||||
}
|
||||
public async updateIntervalAsync(name: JobGroupKeys, cron: string): Promise<void> {
|
||||
logger.info("Updating cron job interval", { name, expression: cron });
|
||||
const job = this.jobGroup.getJobRegistry().get(name);
|
||||
if (!job) throw new Error(`Job ${name} not found`);
|
||||
if (!validateCron(cron)) {
|
||||
throw new Error(`Invalid cron expression: ${cron}`);
|
||||
}
|
||||
if (job.preventCustomInterval && cron !== job.cronExpression) {
|
||||
throw new Error(`Custom cron expressions are not allowed for job ${name}`);
|
||||
}
|
||||
|
||||
const updatedConfig = await this.updateConfigurationAsync(name, { cronExpression: cron });
|
||||
this.jobGroup.getTask(name)?.stop();
|
||||
await this.updateConfigurationAsync(name, { cronExpression: cron });
|
||||
await this.jobGroup.getTask(name)?.destroy();
|
||||
|
||||
this.jobGroup.setTask(
|
||||
name,
|
||||
new Cron(
|
||||
cron,
|
||||
{
|
||||
name,
|
||||
timezone: job.timezone,
|
||||
paused: !updatedConfig.isEnabled,
|
||||
},
|
||||
() => void job.executeAsync(),
|
||||
),
|
||||
schedule(cron, () => void job.executeAsync(), {
|
||||
name,
|
||||
}),
|
||||
);
|
||||
logger.info("Cron job interval updated", { name, expression: cron });
|
||||
}
|
||||
@@ -54,7 +52,7 @@ export class JobManager {
|
||||
if (!job) throw new Error(`Job ${name} not found`);
|
||||
|
||||
await this.updateConfigurationAsync(name, { isEnabled: false });
|
||||
this.jobGroup.stop(name);
|
||||
await this.jobGroup.stopAsync(name);
|
||||
logger.info("Cron job disabled", { name });
|
||||
}
|
||||
public async enableAsync(name: JobGroupKeys): Promise<void> {
|
||||
@@ -88,27 +86,21 @@ export class JobManager {
|
||||
name,
|
||||
configuration: JSON.stringify(configuration),
|
||||
});
|
||||
return {
|
||||
name,
|
||||
cronExpression: configuration.cronExpression ?? existingConfig.cronExpression,
|
||||
isEnabled: configuration.isEnabled ?? existingConfig.isEnabled,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const job = this.jobGroup.getJobRegistry().get(name);
|
||||
if (!job) throw new Error(`Job ${name} not found`);
|
||||
|
||||
const updatedConfig = {
|
||||
await this.db.insert(cronJobConfigurations).values({
|
||||
name,
|
||||
cronExpression: configuration.cronExpression ?? job.cronExpression,
|
||||
isEnabled: configuration.isEnabled ?? true,
|
||||
};
|
||||
await this.db.insert(cronJobConfigurations).values(updatedConfig);
|
||||
});
|
||||
logger.debug("Cron job configuration updated", {
|
||||
name,
|
||||
configuration: JSON.stringify(configuration),
|
||||
});
|
||||
return updatedConfig;
|
||||
}
|
||||
|
||||
public async getAllAsync(): Promise<
|
||||
+41
-1
@@ -1,12 +1,52 @@
|
||||
// This import has to be the first import in the file so that the agent is overridden before any other modules are imported.
|
||||
import "./overrides";
|
||||
|
||||
import { jobGroup } from "@homarr/cron-jobs";
|
||||
import type { FastifyTRPCPluginOptions } from "@trpc/server/adapters/fastify";
|
||||
import { fastifyTRPCPlugin } from "@trpc/server/adapters/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 { 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 { jobGroup } from "@homarr/cron-jobs";
|
||||
import { db } from "@homarr/db";
|
||||
|
||||
import { JobManager } from "./job-manager";
|
||||
import { onStartAsync } from "./on-start";
|
||||
|
||||
const logger = createLogger({ module: "tasksMain" });
|
||||
|
||||
const server = fastify({
|
||||
maxParamLength: 5000,
|
||||
});
|
||||
server.register(fastifyTRPCPlugin, {
|
||||
prefix: CRON_JOB_API_PATH,
|
||||
trpcOptions: {
|
||||
router: jobRouter,
|
||||
createContext: ({ req }) => ({
|
||||
manager: new JobManager(db, jobGroup),
|
||||
apiKey: req.headers[CRON_JOB_API_KEY_HEADER] as string | undefined,
|
||||
}),
|
||||
onError({ path, error }) {
|
||||
logger.error(new ErrorWithMetadata("Error in tasks tRPC handler", { path }, { cause: error }));
|
||||
},
|
||||
} satisfies FastifyTRPCPluginOptions<JobRouter>["trpcOptions"],
|
||||
});
|
||||
|
||||
void (async () => {
|
||||
await onStartAsync();
|
||||
await jobGroup.initializeAsync();
|
||||
await jobGroup.startAllAsync();
|
||||
|
||||
try {
|
||||
await server.listen({ port: CRON_JOB_API_PORT });
|
||||
logger.info("Tasks web server started successfully", { port: CRON_JOB_API_PORT });
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
new ErrorWithMetadata("Failed to start tasks web server", { port: CRON_JOB_API_PORT }, { cause: err }),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
@@ -4,12 +4,10 @@
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/main.ts"
|
||||
},
|
||||
"main": "./src/main.ts",
|
||||
"types": "./src/main.ts",
|
||||
"scripts": {
|
||||
"build": "esbuild src/main.ts --bundle --platform=node --outfile=wssServer.cjs --external:bcrypt --external:@opentelemetry/api --external:deasync --external:cpu-features --loader:.html=text --loader:.scss=text --loader:.node=text",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"dev": "pnpm with-env tsx ./src/main.ts",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
@@ -27,18 +25,18 @@
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"dotenv": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"ws": "catalog:"
|
||||
"dotenv": "^17.4.1",
|
||||
"tsx": "4.20.4",
|
||||
"ws": "^8.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/ws": "catalog:",
|
||||
"esbuild": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/ws": "^8.18.1",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:24.15.0-trixie AS base
|
||||
FROM node:24.14.1-trixie AS base
|
||||
WORKDIR /app
|
||||
COPY package.json .
|
||||
COPY pnpm-lock.yaml .
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"name": "homarr-prebuilt-debian",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^12.9.0"
|
||||
"better-sqlite3": "^12.8.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.2",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"engines": {
|
||||
"node": ">=24.15.0",
|
||||
"pnpm": ">=10.33.2"
|
||||
"node": ">=24.14.1",
|
||||
"pnpm": ">=10.33.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
|
||||
+5
-5
@@ -9,16 +9,16 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
better-sqlite3:
|
||||
specifier: ^12.9.0
|
||||
version: 12.9.0
|
||||
specifier: ^12.8.0
|
||||
version: 12.8.0
|
||||
|
||||
packages:
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
better-sqlite3@12.9.0:
|
||||
resolution: {integrity: sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==}
|
||||
better-sqlite3@12.8.0:
|
||||
resolution: {integrity: sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==}
|
||||
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
|
||||
|
||||
bindings@1.5.0:
|
||||
@@ -148,7 +148,7 @@ snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
better-sqlite3@12.9.0:
|
||||
better-sqlite3@12.8.0:
|
||||
dependencies:
|
||||
bindings: 1.5.0
|
||||
prebuild-install: 7.1.3
|
||||
|
||||
+34
-34
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homarr",
|
||||
"version": "1.60.1-beta.1",
|
||||
"version": "1.59.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "cross-env CI=true turbo build",
|
||||
@@ -15,7 +15,7 @@
|
||||
"db:migration:sqlite:run": "pnpm -F db migration:sqlite:run",
|
||||
"db:push": "pnpm -F db push:sqlite",
|
||||
"db:studio": "pnpm -F db studio",
|
||||
"dev": "pnpm -F nextjs dev",
|
||||
"dev": "turbo dev --parallel",
|
||||
"docker:dev": "docker compose -f ./development/development.docker-compose.yml up",
|
||||
"format": "turbo format --continue -- --cache --cache-location node_modules/.cache/.prettiercache",
|
||||
"format:fix": "turbo format --continue -- --write --cache --cache-location node_modules/.cache/.prettiercache",
|
||||
@@ -36,33 +36,33 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"devDependencies": {
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@semantic-release/changelog": "catalog:",
|
||||
"@semantic-release/commit-analyzer": "catalog:",
|
||||
"@semantic-release/git": "catalog:",
|
||||
"@semantic-release/github": "catalog:",
|
||||
"@semantic-release/npm": "catalog:",
|
||||
"@semantic-release/release-notes-generator": "catalog:",
|
||||
"@testcontainers/redis": "catalog:",
|
||||
"@turbo/gen": "catalog:",
|
||||
"@vitejs/plugin-react": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"@vitest/ui": "catalog:",
|
||||
"conventional-changelog-conventionalcommits": "catalog:",
|
||||
"cross-env": "catalog:",
|
||||
"jsdom": "catalog:",
|
||||
"json5": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"semantic-release": "catalog:",
|
||||
"testcontainers": "catalog:",
|
||||
"turbo": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite-tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@semantic-release/github": "^12.0.6",
|
||||
"@semantic-release/npm": "^13.1.5",
|
||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||
"@testcontainers/redis": "^11.13.0",
|
||||
"@turbo/gen": "^2.9.3",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"@vitest/coverage-v8": "^4.1.2",
|
||||
"@vitest/ui": "^4.1.2",
|
||||
"conventional-changelog-conventionalcommits": "^9.3.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"jsdom": "^29.0.1",
|
||||
"json5": "^2.2.3",
|
||||
"prettier": "^3.8.1",
|
||||
"semantic-release": "^25.0.3",
|
||||
"testcontainers": "^11.13.0",
|
||||
"turbo": "^2.9.3",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-tsconfig-paths": "^6.1.1",
|
||||
"vitest": "^4.1.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.2",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"engines": {
|
||||
"node": ">=24.15.0",
|
||||
"pnpm": ">=10.33.2"
|
||||
"node": ">=24.14.1",
|
||||
"pnpm": ">=10.33.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
@@ -79,23 +79,23 @@
|
||||
"overrides": {
|
||||
"@babel/helpers@<7.26.10": ">=7.29.2",
|
||||
"@babel/runtime@<7.26.10": ">=7.29.2",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.15.2",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.15.0",
|
||||
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.5",
|
||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.5",
|
||||
"esbuild@<=0.24.2": ">=0.28.0",
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
||||
"hono@<4.6.5": ">=4.12.15",
|
||||
"hono@<4.6.5": ">=4.12.10",
|
||||
"linkifyjs@<4.3.2": ">=4.3.2",
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.9",
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.7",
|
||||
"prismjs@<1.30.0": ">=1.30.0",
|
||||
"proxmox-api>undici": "7.24.6",
|
||||
"react-is": "^19.2.5",
|
||||
"rollup@>=4.0.0 <4.22.4": ">=4.60.2",
|
||||
"proxmox-api>undici": "8.0.2",
|
||||
"react-is": "^19.2.4",
|
||||
"rollup@>=4.0.0 <4.22.4": ">=4.60.1",
|
||||
"sha.js@<=2.4.11": ">=2.4.12",
|
||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
|
||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
|
||||
"tmp@<=0.2.3": ">=0.2.5",
|
||||
"vite@>=5.0.0 <=5.4.18": ">=8.0.10"
|
||||
"vite@>=5.0.0 <=5.4.18": ">=8.0.3"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/docker": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@umami/node": "catalog:",
|
||||
"superjson": "catalog:"
|
||||
"@umami/node": "^0.4.0",
|
||||
"superjson": "2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
+18
-18
@@ -9,8 +9,7 @@
|
||||
"./client": "./src/client.ts",
|
||||
"./server": "./src/server.ts",
|
||||
"./websocket": "./src/websocket.ts",
|
||||
"./shared": "./src/shared.ts",
|
||||
"./open-api": "./src/open-api.ts"
|
||||
"./shared": "./src/shared.ts"
|
||||
},
|
||||
"main": "./index.ts",
|
||||
"types": "./index.ts",
|
||||
@@ -25,6 +24,7 @@
|
||||
"@homarr/auth": "workspace:^0.1.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/cron-job-api": "workspace:^0.1.0",
|
||||
"@homarr/cron-job-status": "workspace:^0.1.0",
|
||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
@@ -40,26 +40,26 @@
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@kubernetes/client-node": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@trpc/client": "catalog:",
|
||||
"@trpc/react-query": "catalog:",
|
||||
"@trpc/server": "catalog:",
|
||||
"@trpc/tanstack-react-query": "catalog:",
|
||||
"lodash.clonedeep": "catalog:",
|
||||
"next": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"superjson": "catalog:",
|
||||
"trpc-to-openapi": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@kubernetes/client-node": "^1.4.0",
|
||||
"@tanstack/react-query": "^5.96.2",
|
||||
"@trpc/client": "^11.16.0",
|
||||
"@trpc/react-query": "^11.16.0",
|
||||
"@trpc/server": "^11.16.0",
|
||||
"@trpc/tanstack-react-query": "^11.16.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"next": "16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"superjson": "2.2.6",
|
||||
"trpc-to-openapi": "^3.2.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
|
||||
|
||||
import { openApiDocument } from "./open-api";
|
||||
import type { AppRouter } from "./root";
|
||||
import { appRouter } from "./root";
|
||||
import { createCallerFactory, createTRPCContext } from "./trpc";
|
||||
@@ -29,5 +30,5 @@ type RouterInputs = inferRouterInputs<AppRouter>;
|
||||
**/
|
||||
type RouterOutputs = inferRouterOutputs<AppRouter>;
|
||||
|
||||
export { createTRPCContext, appRouter, createCaller };
|
||||
export { createTRPCContext, appRouter, createCaller, openApiDocument };
|
||||
export type { AppRouter, RouterInputs, RouterOutputs };
|
||||
@@ -2,21 +2,10 @@ import { generateOpenApiDocument } from "trpc-to-openapi";
|
||||
|
||||
import { API_KEY_HEADER_NAME } from "@homarr/auth/api-key";
|
||||
|
||||
import { appRouter } from "./router/app";
|
||||
import { infoRouter } from "./router/info";
|
||||
import { inviteRouter } from "./router/invite";
|
||||
import { userRouter } from "./router/user";
|
||||
import { createTRPCRouter } from "./trpc";
|
||||
|
||||
export const openApiRouter = createTRPCRouter({
|
||||
appRouter,
|
||||
infoRouter,
|
||||
inviteRouter,
|
||||
userRouter,
|
||||
});
|
||||
import { appRouter } from "./root";
|
||||
|
||||
export const openApiDocument = (base: string) =>
|
||||
generateOpenApiDocument(openApiRouter, {
|
||||
generateOpenApiDocument(appRouter, {
|
||||
title: "Homarr API documentation",
|
||||
version: "1.0.0",
|
||||
baseUrl: base,
|
||||
|
||||
@@ -2,62 +2,59 @@ import { observable } from "@trpc/server/observable";
|
||||
import z from "zod/v4";
|
||||
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import { cronExpressionSchema, jobGroupKeys, jobNameSchema } from "@homarr/cron-job-api";
|
||||
import { cronJobApi } from "@homarr/cron-job-api/client";
|
||||
import type { TaskStatus } from "@homarr/cron-job-status";
|
||||
import { createCronJobStatusChannel } from "@homarr/cron-job-status";
|
||||
import { jobGroup } from "@homarr/cron-jobs";
|
||||
import { JobManager } from "@homarr/cron-jobs/job-manager";
|
||||
|
||||
import { createTRPCRouter, permissionRequiredProcedure } from "../trpc";
|
||||
|
||||
const jobGroupKeys = jobGroup.getKeys();
|
||||
const jobNameSchema = z.enum(jobGroup.getKeys());
|
||||
|
||||
const logger = createLogger({ module: "cronJobsRouter" });
|
||||
|
||||
export const cronJobsRouter = createTRPCRouter({
|
||||
triggerJob: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(jobNameSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await new JobManager(ctx.db, jobGroup).triggerAsync(input);
|
||||
.mutation(async ({ input }) => {
|
||||
await cronJobApi.trigger.mutate(input);
|
||||
}),
|
||||
startJob: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(jobNameSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await new JobManager(ctx.db, jobGroup).startAsync(input);
|
||||
.mutation(async ({ input }) => {
|
||||
await cronJobApi.start.mutate(input);
|
||||
}),
|
||||
stopJob: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(jobNameSchema)
|
||||
.mutation(({ input, ctx }) => {
|
||||
new JobManager(ctx.db, jobGroup).stop(input);
|
||||
.mutation(async ({ input }) => {
|
||||
await cronJobApi.stop.mutate(input);
|
||||
}),
|
||||
updateJobInterval: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(
|
||||
z.object({
|
||||
name: jobNameSchema,
|
||||
cron: z.string(),
|
||||
cron: cronExpressionSchema,
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await new JobManager(ctx.db, jobGroup).updateIntervalAsync(input.name, input.cron);
|
||||
.mutation(async ({ input }) => {
|
||||
await cronJobApi.updateInterval.mutate(input);
|
||||
}),
|
||||
disableJob: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(jobNameSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await new JobManager(ctx.db, jobGroup).disableAsync(input);
|
||||
.mutation(async ({ input }) => {
|
||||
await cronJobApi.disable.mutate(input);
|
||||
}),
|
||||
enableJob: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(jobNameSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await new JobManager(ctx.db, jobGroup).enableAsync(input);
|
||||
.mutation(async ({ input }) => {
|
||||
await cronJobApi.enable.mutate(input);
|
||||
}),
|
||||
getJobs: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => {
|
||||
return await new JobManager(ctx.db, jobGroup).getAllAsync();
|
||||
getJobs: permissionRequiredProcedure.requiresPermission("admin").query(async () => {
|
||||
return await cronJobApi.getAll.query();
|
||||
}),
|
||||
subscribeToStatusUpdates: permissionRequiredProcedure.requiresPermission("admin").subscription(() => {
|
||||
return observable<TaskStatus>((emit) => {
|
||||
|
||||
+15
-15
@@ -24,30 +24,30 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@auth/core": "catalog:",
|
||||
"@auth/drizzle-adapter": "catalog:",
|
||||
"@auth/core": "^0.41.1",
|
||||
"@auth/drizzle-adapter": "^1.11.1",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"bcrypt": "catalog:",
|
||||
"cookies": "catalog:",
|
||||
"ldapts": "catalog:",
|
||||
"next": "catalog:",
|
||||
"next-auth": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"bcrypt": "^6.0.0",
|
||||
"cookies": "^0.9.1",
|
||||
"ldapts": "8.1.7",
|
||||
"next": "16.2.3",
|
||||
"next-auth": "5.0.0-beta.30",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/bcrypt": "catalog:",
|
||||
"@types/cookies": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"@types/cookies": "0.9.2",
|
||||
"eslint": "^9.39.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,14 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/api": "workspace:^0.1.0",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,19 +25,19 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@drizzle-team/brocli": "catalog:",
|
||||
"@drizzle-team/brocli": "^0.11.0",
|
||||
"@homarr/auth": "workspace:^0.1.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"dotenv": "catalog:"
|
||||
"dotenv": "^17.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"esbuild": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -27,22 +27,22 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@paralleldrive/cuid2": "catalog:",
|
||||
"cookie": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"next": "catalog:",
|
||||
"octokit": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"undici": "catalog:",
|
||||
"zod": "catalog:",
|
||||
"zod-validation-error": "catalog:"
|
||||
"@paralleldrive/cuid2": "^3.3.0",
|
||||
"cookie": "1.1.1",
|
||||
"dayjs": "^1.11.20",
|
||||
"next": "16.2.3",
|
||||
"octokit": "^5.0.5",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"undici": "8.0.2",
|
||||
"zod": "^4.3.6",
|
||||
"zod-validation-error": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -41,10 +41,6 @@ export class Stopwatch {
|
||||
return dayjs().millisecond(this.startTime).fromNow(true);
|
||||
}
|
||||
|
||||
getElapsedInFormattedMilliseconds() {
|
||||
return `${this.getElapsedInMilliseconds().toFixed(0)}ms`;
|
||||
}
|
||||
|
||||
getElapsedInMilliseconds() {
|
||||
return performance.now() - this.startTime;
|
||||
}
|
||||
|
||||
+14
-14
@@ -37,24 +37,24 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@t3-oss/env-nextjs": "catalog:",
|
||||
"better-sqlite3": "catalog:",
|
||||
"dns-caching": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"ioredis": "catalog:",
|
||||
"mysql2": "catalog:",
|
||||
"pg": "catalog:",
|
||||
"superjson": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@t3-oss/env-nextjs": "^0.13.11",
|
||||
"better-sqlite3": "^12.8.0",
|
||||
"dns-caching": "^0.2.9",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"ioredis": "5.10.1",
|
||||
"mysql2": "3.20.0",
|
||||
"pg": "^8.20.0",
|
||||
"superjson": "2.2.6",
|
||||
"winston": "3.19.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/better-sqlite3": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/pg": "^8.20.0",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,11 @@ export class UndiciHttpAgent extends EnvHttpProxyAgent {
|
||||
private logger: ILogger;
|
||||
|
||||
constructor(props?: HttpAgentOptions) {
|
||||
super(props);
|
||||
super({
|
||||
...props,
|
||||
// See https://github.com/homarr-labs/homarr/issues/5467#issuecomment-4226844346
|
||||
allowH2: false,
|
||||
});
|
||||
this.logger = props?.logger ?? createLogger({ module: "httpAgent" });
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import baseConfig from "@homarr/eslint-config/base";
|
||||
|
||||
/** @type {import('typescript-eslint').Config} */
|
||||
export default [
|
||||
{
|
||||
ignores: [],
|
||||
},
|
||||
...baseConfig,
|
||||
];
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@homarr/cron-job-api",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./env": "./src/env.ts",
|
||||
"./constants": "./src/constants.ts",
|
||||
"./client": "./src/client.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||
"@tanstack/react-query": "^5.96.2",
|
||||
"@trpc/client": "^11.16.0",
|
||||
"@trpc/server": "^11.16.0",
|
||||
"@trpc/tanstack-react-query": "^11.16.0",
|
||||
"node-cron": "^4.2.1",
|
||||
"react": "19.2.4",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/react": "19.2.14",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { createTRPCClient, httpLink } from "@trpc/client";
|
||||
|
||||
import type { JobRouter } from ".";
|
||||
import { CRON_JOB_API_KEY_HEADER, CRON_JOB_API_PATH, CRON_JOB_API_PORT } from "./constants";
|
||||
import { env } from "./env";
|
||||
|
||||
export const cronJobApi = createTRPCClient<JobRouter>({
|
||||
links: [
|
||||
httpLink({
|
||||
url: `${getBaseUrl()}${CRON_JOB_API_PATH}`,
|
||||
headers: {
|
||||
[CRON_JOB_API_KEY_HEADER]: env.CRON_JOB_API_KEY,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
function getBaseUrl() {
|
||||
return `http://localhost:${CRON_JOB_API_PORT}`;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export const CRON_JOB_API_PORT = 3002;
|
||||
export const CRON_JOB_API_PATH = "/trpc";
|
||||
export const CRON_JOB_API_KEY_HEADER = "cron-job-api-key";
|
||||
@@ -0,0 +1,11 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { env as commonEnv } from "@homarr/common/env";
|
||||
import { createEnv } from "@homarr/core/infrastructure/env";
|
||||
|
||||
export const env = createEnv({
|
||||
server: {
|
||||
CRON_JOB_API_KEY: commonEnv.NODE_ENV === "development" ? z.string().default("test") : z.string(),
|
||||
},
|
||||
experimental__runtimeEnv: process.env,
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import { validate } from "node-cron";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import type { JobGroupKeys } from "@homarr/cron-jobs";
|
||||
import { jobGroup } from "@homarr/cron-jobs";
|
||||
|
||||
import { env } from "./env";
|
||||
|
||||
export const jobGroupKeys = jobGroup.getKeys();
|
||||
export const jobNameSchema = z.enum(jobGroup.getKeys());
|
||||
|
||||
export interface IJobManager {
|
||||
startAsync(name: JobGroupKeys): Promise<void>;
|
||||
triggerAsync(name: JobGroupKeys): Promise<void>;
|
||||
stopAsync(name: JobGroupKeys): Promise<void>;
|
||||
updateIntervalAsync(name: JobGroupKeys, cron: string): Promise<void>;
|
||||
disableAsync(name: JobGroupKeys): Promise<void>;
|
||||
enableAsync(name: JobGroupKeys): Promise<void>;
|
||||
getAllAsync(): Promise<
|
||||
{
|
||||
name: JobGroupKeys;
|
||||
cron: string;
|
||||
preventManualExecution: boolean;
|
||||
preventCustomInterval: boolean;
|
||||
isEnabled: boolean;
|
||||
}[]
|
||||
>;
|
||||
}
|
||||
|
||||
const t = initTRPC
|
||||
.context<{
|
||||
manager: IJobManager;
|
||||
apiKey?: string;
|
||||
}>()
|
||||
.create();
|
||||
|
||||
const createTrpcRouter = t.router;
|
||||
const apiKeyProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (ctx.apiKey !== env.CRON_JOB_API_KEY) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: "Missing or invalid API key",
|
||||
});
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
apiKey: undefined, // Clear the API key after checking
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
export const cronExpressionSchema = z.string().refine((expression) => validate(expression), {
|
||||
error: "Invalid cron expression",
|
||||
});
|
||||
|
||||
export const jobRouter = createTrpcRouter({
|
||||
start: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => {
|
||||
await ctx.manager.startAsync(input);
|
||||
}),
|
||||
trigger: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => {
|
||||
await ctx.manager.triggerAsync(input);
|
||||
}),
|
||||
stop: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => {
|
||||
await ctx.manager.stopAsync(input);
|
||||
}),
|
||||
updateInterval: apiKeyProcedure
|
||||
.input(
|
||||
z.object({
|
||||
name: jobNameSchema,
|
||||
cron: cronExpressionSchema,
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await ctx.manager.updateIntervalAsync(input.name, input.cron);
|
||||
}),
|
||||
disable: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => {
|
||||
await ctx.manager.disableAsync(input);
|
||||
}),
|
||||
enable: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => {
|
||||
await ctx.manager.enableAsync(input);
|
||||
}),
|
||||
getAll: apiKeyProcedure.query(({ ctx }) => {
|
||||
return ctx.manager.getAllAsync();
|
||||
}),
|
||||
});
|
||||
|
||||
export type JobRouter = typeof jobRouter;
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@homarr/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
"include": ["*.ts", "src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -27,13 +27,14 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"croner": "catalog:"
|
||||
"node-cron": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Cron } from "croner";
|
||||
import { createTask, validate } from "node-cron";
|
||||
|
||||
import { Stopwatch } from "@homarr/common";
|
||||
import type { MaybePromise } from "@homarr/common/types";
|
||||
@@ -83,15 +83,10 @@ const createCallback = <TAllowedNames extends string, TName extends TAllowedName
|
||||
? defaultCronExpression
|
||||
: (configuration?.cronExpression ?? defaultCronExpression);
|
||||
|
||||
const scheduledTask = new Cron(
|
||||
cronExpression,
|
||||
{
|
||||
name,
|
||||
timezone: creatorOptions.timezone,
|
||||
paused: true,
|
||||
},
|
||||
() => void catchingCallbackAsync(),
|
||||
);
|
||||
const scheduledTask = createTask(cronExpression, () => void catchingCallbackAsync(), {
|
||||
name,
|
||||
timezone: creatorOptions.timezone,
|
||||
});
|
||||
creatorOptions.logger.logDebug("The scheduled task for cron job was created", {
|
||||
name,
|
||||
cronExpression: defaultCronExpression,
|
||||
@@ -121,7 +116,6 @@ const createCallback = <TAllowedNames extends string, TName extends TAllowedName
|
||||
},
|
||||
preventManualExecution: options.preventManualExecution ?? false,
|
||||
preventCustomInterval: options.preventCustomInterval ?? false,
|
||||
timezone: creatorOptions.timezone,
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -138,6 +132,13 @@ export const createCronJobCreator = <TAllowedNames extends string = string>(
|
||||
defaultCronExpression: TExpression,
|
||||
options: CreateCronJobOptions = { runOnStart: false },
|
||||
) => {
|
||||
creatorOptions.logger.logDebug("Validating cron expression for cron job", {
|
||||
name,
|
||||
cronExpression: defaultCronExpression,
|
||||
});
|
||||
if (!validate(defaultCronExpression)) {
|
||||
throw new Error(`Invalid cron expression '${defaultCronExpression}' for job '${name}'`);
|
||||
}
|
||||
creatorOptions.logger.logDebug("Cron job expression for cron job is valid", {
|
||||
name,
|
||||
cronExpression: defaultCronExpression,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Cron } from "croner";
|
||||
import type { ScheduledTask } from "node-cron";
|
||||
|
||||
import { objectEntries, objectKeys } from "@homarr/common";
|
||||
import { db } from "@homarr/db";
|
||||
@@ -34,7 +34,7 @@ export const createJobGroupCreator = <TAllowedNames extends string = string>(
|
||||
});
|
||||
}
|
||||
|
||||
const tasks = new Map<string, Cron>();
|
||||
const tasks = new Map<string, ScheduledTask>();
|
||||
|
||||
return {
|
||||
initializeAsync: async () => {
|
||||
@@ -62,7 +62,7 @@ export const createJobGroupCreator = <TAllowedNames extends string = string>(
|
||||
name: job.name,
|
||||
});
|
||||
await job.onStartAsync();
|
||||
tasks.get(name as string)?.resume();
|
||||
await tasks.get(name as string)?.start();
|
||||
},
|
||||
startAllAsync: async () => {
|
||||
for (const job of jobRegistry.values()) {
|
||||
@@ -74,7 +74,7 @@ export const createJobGroupCreator = <TAllowedNames extends string = string>(
|
||||
name: job.name,
|
||||
});
|
||||
await job.onStartAsync();
|
||||
tasks.get(job.name)?.resume();
|
||||
await tasks.get(job.name)?.start();
|
||||
}
|
||||
},
|
||||
runManuallyAsync: async (name: keyof TJobs) => {
|
||||
@@ -87,23 +87,23 @@ export const createJobGroupCreator = <TAllowedNames extends string = string>(
|
||||
options.logger.logInfo("Running schedule cron job manually.", {
|
||||
name: job.name,
|
||||
});
|
||||
await tasks.get(name as string)?.trigger();
|
||||
await tasks.get(name as string)?.execute();
|
||||
},
|
||||
stop: (name: keyof TJobs) => {
|
||||
stopAsync: async (name: keyof TJobs) => {
|
||||
const job = jobRegistry.get(name as string);
|
||||
if (!job) return;
|
||||
|
||||
options.logger.logInfo("Stopping schedule of cron job.", {
|
||||
name: job.name,
|
||||
});
|
||||
tasks.get(name as string)?.pause();
|
||||
await tasks.get(name as string)?.stop();
|
||||
},
|
||||
stopAll: () => {
|
||||
stopAllAsync: async () => {
|
||||
for (const job of jobRegistry.values()) {
|
||||
options.logger.logInfo("Stopping schedule of cron job.", {
|
||||
name: job.name,
|
||||
});
|
||||
tasks.get(job.name)?.pause();
|
||||
await tasks.get(job.name)?.stop();
|
||||
}
|
||||
},
|
||||
getJobRegistry() {
|
||||
@@ -112,8 +112,8 @@ export const createJobGroupCreator = <TAllowedNames extends string = string>(
|
||||
getTask(name: keyof TJobs) {
|
||||
return tasks.get(name as string) ?? null;
|
||||
},
|
||||
setTask(name: keyof TJobs, cron: Cron) {
|
||||
tasks.set(name as string, cron);
|
||||
setTask(name: keyof TJobs, task: ScheduledTask) {
|
||||
tasks.set(name as string, task);
|
||||
},
|
||||
getKeys() {
|
||||
return objectKeys(jobs);
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./index.ts",
|
||||
"./job-manager": "./src/job-manager.ts"
|
||||
".": "./index.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
@@ -39,14 +38,13 @@
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"semver-parser": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"semver-parser": "^4.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -27,44 +27,33 @@ import { updateCheckerJob } from "./jobs/update-checker";
|
||||
import { weatherJob } from "./jobs/weather";
|
||||
import { createCronJobGroup } from "./lib";
|
||||
|
||||
const getJobGroup = () => {
|
||||
return createCronJobGroup({
|
||||
analytics: analyticsJob,
|
||||
iconsUpdater: iconsUpdaterJob,
|
||||
ping: pingJob,
|
||||
smartHomeEntityState: smartHomeEntityStateJob,
|
||||
mediaServer: mediaServerJob,
|
||||
mediaOrganizer: mediaOrganizerJob,
|
||||
downloads: downloadsJob,
|
||||
dnsHole: dnsHoleJob,
|
||||
mediaRequestStats: mediaRequestStatsJob,
|
||||
mediaRequestList: mediaRequestListJob,
|
||||
rssFeeds: rssFeedsJob,
|
||||
indexerManager: indexerManagerJob,
|
||||
healthMonitoring: healthMonitoringJob,
|
||||
updateChecker: updateCheckerJob,
|
||||
mediaTranscoding: mediaTranscodingJob,
|
||||
minecraftServerStatus: minecraftServerStatusJob,
|
||||
dockerContainers: dockerContainersJob,
|
||||
networkController: networkControllerJob,
|
||||
firewallCpu: firewallCpuJob,
|
||||
firewallMemory: firewallMemoryJob,
|
||||
firewallVersion: firewallVersionJob,
|
||||
firewallInterfaces: firewallInterfacesJob,
|
||||
refreshNotifications: refreshNotificationsJob,
|
||||
weather: weatherJob,
|
||||
timetable: timetableJob,
|
||||
tracearr: tracearrJob,
|
||||
});
|
||||
};
|
||||
|
||||
// Add global type augmentation for homarr
|
||||
declare global {
|
||||
var cronJobs: ReturnType<typeof getJobGroup> | undefined;
|
||||
}
|
||||
|
||||
global.cronJobs ??= getJobGroup();
|
||||
|
||||
export const jobGroup = global.cronJobs;
|
||||
export const jobGroup = createCronJobGroup({
|
||||
analytics: analyticsJob,
|
||||
iconsUpdater: iconsUpdaterJob,
|
||||
ping: pingJob,
|
||||
smartHomeEntityState: smartHomeEntityStateJob,
|
||||
mediaServer: mediaServerJob,
|
||||
mediaOrganizer: mediaOrganizerJob,
|
||||
downloads: downloadsJob,
|
||||
dnsHole: dnsHoleJob,
|
||||
mediaRequestStats: mediaRequestStatsJob,
|
||||
mediaRequestList: mediaRequestListJob,
|
||||
rssFeeds: rssFeedsJob,
|
||||
indexerManager: indexerManagerJob,
|
||||
healthMonitoring: healthMonitoringJob,
|
||||
updateChecker: updateCheckerJob,
|
||||
mediaTranscoding: mediaTranscodingJob,
|
||||
minecraftServerStatus: minecraftServerStatusJob,
|
||||
dockerContainers: dockerContainersJob,
|
||||
networkController: networkControllerJob,
|
||||
firewallCpu: firewallCpuJob,
|
||||
firewallMemory: firewallMemoryJob,
|
||||
firewallVersion: firewallVersionJob,
|
||||
firewallInterfaces: firewallInterfacesJob,
|
||||
refreshNotifications: refreshNotificationsJob,
|
||||
weather: weatherJob,
|
||||
timetable: timetableJob,
|
||||
tracearr: tracearrJob,
|
||||
});
|
||||
|
||||
export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number];
|
||||
@@ -1,12 +1,9 @@
|
||||
import type { MySqlRawQueryResult } from "drizzle-orm/mysql2";
|
||||
import type { QueryResult } from "pg";
|
||||
|
||||
import { createId, splitToNChunks, Stopwatch } from "@homarr/common";
|
||||
import { env } from "@homarr/common/env";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import { EVERY_WEEK } from "@homarr/cron-jobs-core/expressions";
|
||||
import type { InferInsertModel } from "@homarr/db";
|
||||
import { and, db, eq, handleTransactionsAsync, inArray, notInArray, or } from "@homarr/db";
|
||||
import { db, handleTransactionsAsync, inArray, sql } from "@homarr/db";
|
||||
import { iconRepositories, icons } from "@homarr/db/schema";
|
||||
import { fetchIconsAsync } from "@homarr/icons";
|
||||
|
||||
@@ -29,7 +26,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
logger.info("Fetched icons from repositories", {
|
||||
repositoryCount: repositoryIconGroups.length,
|
||||
iconCount: countIcons,
|
||||
duration: stopWatch.getElapsedInFormattedMilliseconds(),
|
||||
duration: stopWatch.getElapsedInHumanWords(),
|
||||
});
|
||||
|
||||
const databaseIconRepositories = await db.query.iconRepositories.findMany({
|
||||
@@ -38,7 +35,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
},
|
||||
});
|
||||
|
||||
const skippedChecksums = new Map<string, string[]>();
|
||||
const skippedChecksums: `${string}.${string}`[] = [];
|
||||
let countDeleted = 0;
|
||||
let countInserted = 0;
|
||||
|
||||
@@ -53,7 +50,6 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
if (!repositoryIconGroup.success) {
|
||||
continue;
|
||||
}
|
||||
const localSkippedChecksums: string[] = [];
|
||||
|
||||
const repositoryInDb = databaseIconRepositories.find(
|
||||
(dbIconGroup) => dbIconGroup.slug === repositoryIconGroup.slug,
|
||||
@@ -70,7 +66,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
|
||||
for (const icon of repositoryIconGroup.icons) {
|
||||
if (dbIconsInRepository.some((dbIcon) => dbIcon.checksum === icon.checksum)) {
|
||||
localSkippedChecksums.push(icon.checksum);
|
||||
skippedChecksums.push(`${iconRepositoryId}.${icon.checksum}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -83,38 +79,19 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
});
|
||||
countInserted++;
|
||||
}
|
||||
|
||||
skippedChecksums.set(iconRepositoryId, localSkippedChecksums);
|
||||
}
|
||||
|
||||
const deadIcons = allDbIcons.filter(
|
||||
(icon) => !skippedChecksums.includes(`${icon.iconRepositoryId}.${icon.checksum}`),
|
||||
);
|
||||
|
||||
const deadIconRepositories = databaseIconRepositories.filter(
|
||||
(iconRepository) => !repositoryIconGroups.some((group) => group.slug === iconRepository.slug),
|
||||
);
|
||||
|
||||
const deadIconsFilter = or(
|
||||
...[...skippedChecksums.entries()].map(([iconRepositoryId, checksums]) =>
|
||||
and(eq(icons.iconRepositoryId, iconRepositoryId), notInArray(icons.checksum, checksums)),
|
||||
),
|
||||
);
|
||||
|
||||
await handleTransactionsAsync(db, {
|
||||
async handleAsync(db, schema) {
|
||||
await db.transaction(async (transaction) => {
|
||||
const result = (await transaction.delete(schema.icons).where(deadIconsFilter)) as
|
||||
| MySqlRawQueryResult
|
||||
| QueryResult;
|
||||
|
||||
countDeleted += Array.isArray(result) ? result[0].affectedRows : (result.rowCount ?? 0);
|
||||
|
||||
if (deadIconRepositories.length >= 1) {
|
||||
await transaction.delete(schema.iconRepositories).where(
|
||||
inArray(
|
||||
iconRepositories.id,
|
||||
deadIconRepositories.map((iconRepository) => iconRepository.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (newIconRepositories.length >= 1) {
|
||||
await transaction.insert(schema.iconRepositories).values(newIconRepositories);
|
||||
}
|
||||
@@ -125,12 +102,52 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
await transaction.insert(schema.icons).values(chunck);
|
||||
}
|
||||
}
|
||||
if (deadIcons.length >= 1) {
|
||||
await transaction.delete(schema.icons).where(
|
||||
inArray(
|
||||
// Combine iconRepositoryId and checksum to allow same icons on different repositories
|
||||
sql`concat(${icons.iconRepositoryId}, '.', ${icons.checksum})`,
|
||||
deadIcons.map((icon) => `${icon.iconRepositoryId}.${icon.checksum}`),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (deadIconRepositories.length >= 1) {
|
||||
await transaction.delete(schema.iconRepositories).where(
|
||||
inArray(
|
||||
iconRepositories.id,
|
||||
deadIconRepositories.map((iconRepository) => iconRepository.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
countDeleted += deadIcons.length;
|
||||
});
|
||||
},
|
||||
handleSync() {
|
||||
db.transaction((transaction) => {
|
||||
const result = transaction.delete(icons).where(deadIconsFilter).run();
|
||||
countDeleted += result.changes;
|
||||
if (newIconRepositories.length >= 1) {
|
||||
transaction.insert(iconRepositories).values(newIconRepositories).run();
|
||||
}
|
||||
|
||||
if (newIcons.length >= 1) {
|
||||
// We only insert 5000 icons at a time to avoid SQLite limitations
|
||||
for (const chunck of splitToNChunks(newIcons, Math.ceil(newIcons.length / 5000))) {
|
||||
transaction.insert(icons).values(chunck).run();
|
||||
}
|
||||
}
|
||||
if (deadIcons.length >= 1) {
|
||||
transaction
|
||||
.delete(icons)
|
||||
.where(
|
||||
inArray(
|
||||
// Combine iconRepositoryId and checksum to allow same icons on different repositories
|
||||
sql`concat(${icons.iconRepositoryId}, '.', ${icons.checksum})`,
|
||||
deadIcons.map((icon) => `${icon.iconRepositoryId}.${icon.checksum}`),
|
||||
),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
if (deadIconRepositories.length >= 1) {
|
||||
transaction
|
||||
@@ -144,22 +161,13 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
||||
.run();
|
||||
}
|
||||
|
||||
if (newIconRepositories.length >= 1) {
|
||||
transaction.insert(iconRepositories).values(newIconRepositories).run();
|
||||
}
|
||||
|
||||
if (newIcons.length >= 1) {
|
||||
// We only insert 5000 icons at a time to avoid SQLite limitations
|
||||
for (const chunck of splitToNChunks(newIcons, Math.ceil(newIcons.length / 5000))) {
|
||||
transaction.insert(icons).values(chunck).run();
|
||||
}
|
||||
}
|
||||
countDeleted += deadIcons.length;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("Updated icons in database", {
|
||||
duration: stopWatch.getElapsedInFormattedMilliseconds(),
|
||||
duration: stopWatch.getElapsedInHumanWords(),
|
||||
added: countInserted,
|
||||
deleted: countDeleted,
|
||||
});
|
||||
|
||||
+21
-23
@@ -37,42 +37,40 @@
|
||||
"push:sqlite": "pnpm with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
||||
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
||||
"studio": "pnpm with-env drizzle-kit studio --config ./configs/sqlite.config.ts",
|
||||
"studio:mysql": "pnpm with-env drizzle-kit studio --config ./configs/mysql.config.ts",
|
||||
"studio:postgresql": "pnpm with-env drizzle-kit studio --config ./configs/postgresql.config.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@auth/core": "catalog:",
|
||||
"@auth/core": "^0.41.1",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@mantine/core": "catalog:",
|
||||
"@paralleldrive/cuid2": "catalog:",
|
||||
"@testcontainers/mysql": "catalog:",
|
||||
"@testcontainers/postgresql": "catalog:",
|
||||
"better-sqlite3": "catalog:",
|
||||
"dotenv": "catalog:",
|
||||
"drizzle-kit": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"drizzle-zod": "catalog:",
|
||||
"mysql2": "catalog:",
|
||||
"pg": "catalog:",
|
||||
"superjson": "catalog:"
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@paralleldrive/cuid2": "^3.3.0",
|
||||
"@testcontainers/mysql": "^11.13.0",
|
||||
"@testcontainers/postgresql": "^11.13.0",
|
||||
"better-sqlite3": "^12.8.0",
|
||||
"dotenv": "^17.4.1",
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"mysql2": "3.20.0",
|
||||
"pg": "^8.20.0",
|
||||
"superjson": "2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/better-sqlite3": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"dotenv-cli": "catalog:",
|
||||
"esbuild": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/pg": "^8.20.0",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.4",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -24,15 +24,15 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"fast-xml-parser": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"fast-xml-parser": "^5.5.10",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -26,14 +26,14 @@
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"dockerode": "catalog:"
|
||||
"dockerode": "^4.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/dockerode": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/dockerode": "^4.0.1",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,15 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/form": "catalog:",
|
||||
"mantine-form-zod-resolver": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@mantine/form": "^8.3.18",
|
||||
"mantine-form-zod-resolver": "^1.3.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -30,15 +30,15 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "catalog:",
|
||||
"react": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@mantine/core": "^8.3.18",
|
||||
"react": "19.2.4",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,14 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"bcrypt": "catalog:"
|
||||
"bcrypt": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/bcrypt": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,39 +25,39 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@ctrl/deluge": "catalog:",
|
||||
"@ctrl/qbittorrent": "catalog:",
|
||||
"@ctrl/transmission": "catalog:",
|
||||
"@gitbeaker/rest": "catalog:",
|
||||
"@ctrl/deluge": "^8.0.0",
|
||||
"@ctrl/qbittorrent": "^9.11.2",
|
||||
"@ctrl/transmission": "^7.4.0",
|
||||
"@gitbeaker/rest": "^43.8.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/image-proxy": "workspace:^0.1.0",
|
||||
"@homarr/node-unifi": "catalog:",
|
||||
"@homarr/node-unifi": "^2.6.0",
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@immich/sdk": "catalog:",
|
||||
"@jellyfin/sdk": "catalog:",
|
||||
"@octokit/auth-app": "catalog:",
|
||||
"ical.js": "catalog:",
|
||||
"maria2": "catalog:",
|
||||
"octokit": "catalog:",
|
||||
"proxmox-api": "catalog:",
|
||||
"timezones-ical-library": "catalog:",
|
||||
"tsdav": "catalog:",
|
||||
"undici": "catalog:",
|
||||
"xml2js": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@immich/sdk": "^2.6.3",
|
||||
"@jellyfin/sdk": "^0.13.0",
|
||||
"@octokit/auth-app": "^8.2.0",
|
||||
"ical.js": "^2.2.1",
|
||||
"maria2": "^0.4.1",
|
||||
"octokit": "^5.0.5",
|
||||
"proxmox-api": "1.1.1",
|
||||
"timezones-ical-library": "^2.1.3",
|
||||
"tsdav": "^2.1.8",
|
||||
"undici": "8.0.2",
|
||||
"xml2js": "^0.6.2",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node-unifi": "catalog:",
|
||||
"@types/xml2js": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/node-unifi": "^2.5.1",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -113,9 +113,7 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
|
||||
// next actually returns undefined when there are no more occurrences
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
while ((next = iterator.next())) {
|
||||
const nextStartDate = next.isDate
|
||||
? next.toJSDate()
|
||||
: next.convertToZone(ICAL.Timezone.utcTimezone).toJSDate();
|
||||
const nextStartDate = next.convertToZone(ICAL.Timezone.utcTimezone).toJSDate();
|
||||
if (nextStartDate > end) break;
|
||||
const nextEndDate = new Date(nextStartDate.getTime() + durationMs);
|
||||
if (nextEndDate < start) continue;
|
||||
@@ -123,11 +121,7 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
|
||||
startDates.push(nextStartDate);
|
||||
}
|
||||
} else {
|
||||
startDates = [
|
||||
veventObject.startDate.isDate
|
||||
? veventObject.startDate.toJSDate()
|
||||
: veventObject.startDate.convertToZone(ICAL.Timezone.utcTimezone).toJSDate(),
|
||||
];
|
||||
startDates = [veventObject.startDate.convertToZone(ICAL.Timezone.utcTimezone).toJSDate()];
|
||||
}
|
||||
|
||||
return startDates.map((startDate) => {
|
||||
|
||||
@@ -33,19 +33,19 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "catalog:",
|
||||
"@tabler/icons-react": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"next": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@tabler/icons-react": "^3.41.1",
|
||||
"dayjs": "^1.11.20",
|
||||
"next": "16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -24,15 +24,15 @@
|
||||
"dependencies": {
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/core": "catalog:",
|
||||
"@mantine/hooks": "catalog:",
|
||||
"react": "catalog:"
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@mantine/hooks": "^8.3.18",
|
||||
"react": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,14 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/notifications": "catalog:",
|
||||
"@tabler/icons-react": "catalog:"
|
||||
"@mantine/notifications": "^8.3.18",
|
||||
"@tabler/icons-react": "^3.41.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -37,22 +37,22 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "catalog:",
|
||||
"@mantine/hooks": "catalog:",
|
||||
"adm-zip": "catalog:",
|
||||
"next": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"superjson": "catalog:",
|
||||
"zod": "catalog:",
|
||||
"zod-form-data": "catalog:"
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@mantine/hooks": "^8.3.18",
|
||||
"adm-zip": "0.5.17",
|
||||
"next": "16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"superjson": "2.2.6",
|
||||
"zod": "^4.3.6",
|
||||
"zod-form-data": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/adm-zip": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@types/adm-zip": "0.5.8",
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,13 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"zod": "catalog:"
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -26,14 +26,14 @@
|
||||
"@homarr/core": "workspace:^",
|
||||
"@homarr/db": "workspace:^",
|
||||
"@homarr/definitions": "workspace:^",
|
||||
"ioredis": "catalog:",
|
||||
"superjson": "catalog:"
|
||||
"ioredis": "5.10.1",
|
||||
"superjson": "2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@extractus/feed-extractor": "catalog:",
|
||||
"@extractus/feed-extractor": "7.1.7",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
@@ -30,17 +30,17 @@
|
||||
"@homarr/docker": "workspace:^0.1.0",
|
||||
"@homarr/integrations": "workspace:^0.1.0",
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"dayjs": "catalog:",
|
||||
"octokit": "catalog:",
|
||||
"superjson": "catalog:",
|
||||
"undici": "catalog:",
|
||||
"zod": "catalog:"
|
||||
"dayjs": "^1.11.20",
|
||||
"octokit": "^5.0.5",
|
||||
"superjson": "2.2.6",
|
||||
"undici": "8.0.2",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,16 +25,16 @@
|
||||
"dependencies": {
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@mantine/dates": "catalog:",
|
||||
"next": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
"@mantine/dates": "^8.3.18",
|
||||
"next": "16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -33,21 +33,21 @@
|
||||
"@homarr/settings": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/core": "catalog:",
|
||||
"@mantine/hooks": "catalog:",
|
||||
"@mantine/spotlight": "catalog:",
|
||||
"@tabler/icons-react": "catalog:",
|
||||
"jotai": "catalog:",
|
||||
"next": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"use-deep-compare-effect": "catalog:"
|
||||
"@mantine/core": "^8.3.18",
|
||||
"@mantine/hooks": "^8.3.18",
|
||||
"@mantine/spotlight": "^8.3.18",
|
||||
"@tabler/icons-react": "^3.41.1",
|
||||
"jotai": "^2.19.0",
|
||||
"next": "16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"use-deep-compare-effect": "^1.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,19 +29,19 @@
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"dayjs": "catalog:",
|
||||
"deepmerge": "catalog:",
|
||||
"mantine-react-table": "catalog:",
|
||||
"next": "catalog:",
|
||||
"next-intl": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
"dayjs": "^1.11.20",
|
||||
"deepmerge": "4.3.1",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "16.2.3",
|
||||
"next-intl": "4.9.0",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"eslint": "^9.39.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": "显示标题"
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": "显示最近更新"
|
||||
}
|
||||
},
|
||||
"empty": "选择要显示的笔记",
|
||||
"emptyContent": "此笔记没有内容",
|
||||
"notFound": "未找到笔记",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "更新时间:{date}",
|
||||
"untitled": "未命名",
|
||||
"edit": "编辑",
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "嵌入互联网上的任何内容。某些网站可能限制访问。",
|
||||
@@ -1901,7 +1873,7 @@
|
||||
"label": "显示内存信息"
|
||||
},
|
||||
"gpu": {
|
||||
"label": "显示 GPU 信息"
|
||||
"label": ""
|
||||
},
|
||||
"showUptime": {
|
||||
"label": "显示运行时间"
|
||||
@@ -2200,15 +2172,15 @@
|
||||
"description": ""
|
||||
},
|
||||
"showPhotos": {
|
||||
"label": "显示图片",
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"showVideos": {
|
||||
"label": "显示视频",
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"showStorage": {
|
||||
"label": "显示存储",
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
@@ -2229,7 +2201,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"showPhotoInfo": {
|
||||
"label": "显示图片信息",
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "隐藏描述"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "没有可显示的通知。",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "下载",
|
||||
"upload": "上传",
|
||||
"ping": "Ping",
|
||||
"status": "状态",
|
||||
"avgDownload": "平均下载",
|
||||
"avgUpload": "平均上传",
|
||||
"avgPing": "平均Ping",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "失败",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "系统资源",
|
||||
"description": "您系统的 CPU、内存、磁盘及其他硬件使用情况",
|
||||
@@ -2812,7 +2742,7 @@
|
||||
"option": {
|
||||
"cpu": "CPU",
|
||||
"memory": "内存",
|
||||
"gpu": "GPU",
|
||||
"gpu": "",
|
||||
"network": "网络"
|
||||
}
|
||||
},
|
||||
@@ -2829,7 +2759,7 @@
|
||||
"card": {
|
||||
"cpu": "CPU",
|
||||
"memory": "内存",
|
||||
"gpu": "GPU",
|
||||
"gpu": "",
|
||||
"network": "网络",
|
||||
"up": "上传",
|
||||
"down": "下载"
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "翻译人员",
|
||||
"subtitle": "在许多语言中,有{count}人贡献了翻译"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "相关的库",
|
||||
"subtitle": "{count} 用于 Homarr 代码中"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "crwdns4064:0crwdne4064:0",
|
||||
"description": "crwdns4066:0crwdne4066:0",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": "crwdns4068:0crwdne4068:0"
|
||||
},
|
||||
"showTitle": {
|
||||
"label": "crwdns4070:0crwdne4070:0"
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": "crwdns4072:0crwdne4072:0"
|
||||
}
|
||||
},
|
||||
"empty": "crwdns4074:0crwdne4074:0",
|
||||
"emptyContent": "crwdns4076:0crwdne4076:0",
|
||||
"notFound": "crwdns4078:0crwdne4078:0",
|
||||
"noNotes": "crwdns4080:0crwdne4080:0",
|
||||
"integrationRequired": "crwdns4082:0crwdne4082:0",
|
||||
"updatedAt": "crwdns4084:0{date}crwdne4084:0",
|
||||
"untitled": "crwdns4086:0crwdne4086:0",
|
||||
"edit": "crwdns4088:0crwdne4088:0",
|
||||
"save": "crwdns4090:0crwdne4090:0",
|
||||
"cancel": "crwdns4092:0crwdne4092:0",
|
||||
"readOnlyViewer": "crwdns4094:0crwdne4094:0",
|
||||
"saveForbidden": "crwdns4096:0crwdne4096:0",
|
||||
"saveFailed": "crwdns4098:0crwdne4098:0"
|
||||
},
|
||||
"iframe": {
|
||||
"name": "crwdns950:0crwdne950:0",
|
||||
"description": "crwdns952:0crwdne952:0",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "crwdns3674:0crwdne3674:0"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": "crwdns4012:0crwdne4012:0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "crwdns3480:0crwdne3480:0",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "crwdns4014:0crwdne4014:0",
|
||||
"description": "crwdns4016:0crwdne4016:0",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": "crwdns4018:0crwdne4018:0"
|
||||
},
|
||||
"showStats": {
|
||||
"label": "crwdns4020:0crwdne4020:0"
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": "crwdns4022:0crwdne4022:0"
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": "crwdns4024:0crwdne4024:0"
|
||||
}
|
||||
},
|
||||
"latestResult": "crwdns4026:0crwdne4026:0",
|
||||
"averages": "crwdns4028:0crwdne4028:0",
|
||||
"tests": "crwdns4030:0{count}crwdne4030:0",
|
||||
"recentResults": "crwdns4032:0crwdne4032:0",
|
||||
"recentResultsSpeed": "crwdns4034:0crwdne4034:0",
|
||||
"recentResultsPing": "crwdns4036:0crwdne4036:0",
|
||||
"download": "crwdns4038:0crwdne4038:0",
|
||||
"upload": "crwdns4040:0crwdne4040:0",
|
||||
"ping": "crwdns4042:0crwdne4042:0",
|
||||
"status": "crwdns4044:0crwdne4044:0",
|
||||
"avgDownload": "crwdns4046:0crwdne4046:0",
|
||||
"avgUpload": "crwdns4048:0crwdne4048:0",
|
||||
"avgPing": "crwdns4050:0crwdne4050:0",
|
||||
"healthy": "crwdns4052:0crwdne4052:0",
|
||||
"unhealthy": "crwdns4054:0crwdne4054:0",
|
||||
"ok": "crwdns4056:0crwdne4056:0",
|
||||
"fail": "crwdns4058:0crwdne4058:0",
|
||||
"noSectionsEnabled": "crwdns4060:0crwdne4060:0",
|
||||
"error": {
|
||||
"internalServerError": "crwdns4062:0crwdne4062:0"
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "crwdns3600:0crwdne3600:0",
|
||||
"description": "crwdns3602:0crwdne3602:0",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "crwdns1884:0crwdne1884:0",
|
||||
"subtitle": "crwdns1886:0{count}crwdne1886:0"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "crwdns4100:0crwdne4100:0",
|
||||
"subtitle": "crwdns4102:0{count}crwdne4102:0"
|
||||
},
|
||||
"libraries": {
|
||||
"title": "crwdns1888:0crwdne1888:0",
|
||||
"subtitle": "crwdns1890:0{count}crwdne1890:0"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "Vložte jakýkoli obsah z internetu. Některé webové stránky mohou omezit přístup.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "Překladatelé",
|
||||
"subtitle": "{count} přispívá překladem do mnoha jazyků"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "Anchor note",
|
||||
"description": "Viser en valgt note fra Anchor",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": "Note"
|
||||
},
|
||||
"showTitle": {
|
||||
"label": "Vis titel"
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": "Vis senest opdateret"
|
||||
}
|
||||
},
|
||||
"empty": "Vælg en note der skal vises",
|
||||
"emptyContent": "Denne note har intet indhold",
|
||||
"notFound": "Note ikke fundet",
|
||||
"noNotes": "Ingen noter fundet",
|
||||
"integrationRequired": "Vælg først en Anchorintegration",
|
||||
"updatedAt": "Opdateret {date}",
|
||||
"untitled": "Unavngivet",
|
||||
"edit": "Rediger",
|
||||
"save": "Gem",
|
||||
"cancel": "Annullér",
|
||||
"readOnlyViewer": "Du har læseadgang til denne note. Redigering er deaktiveret.",
|
||||
"saveForbidden": "Du har ikke adgang til at redigere denne note.",
|
||||
"saveFailed": "Kunne ikke gemme denne note. Prøv igen."
|
||||
},
|
||||
"iframe": {
|
||||
"name": "indlejret dokument (iframe)",
|
||||
"description": "Indlejr ethvert indhold fra internettet. Nogle websteder kan begrænse adgang.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "Skjul beskrivelse"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": "Vis poster billede"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "Ingen notifikationer at vise.",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "Speedtest Tracker",
|
||||
"description": "Viser testresultater for hastighed fra din Speedtest Tracker instans.",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": "Vis seneste resultat"
|
||||
},
|
||||
"showStats": {
|
||||
"label": "Vis gennemsnit"
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": "Vis seneste resultater"
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": "Vis ping-graf"
|
||||
}
|
||||
},
|
||||
"latestResult": "Seneste resultat",
|
||||
"averages": "Gennemsnit",
|
||||
"tests": "{count} tests",
|
||||
"recentResults": "Seneste resultater",
|
||||
"recentResultsSpeed": "Hastighed",
|
||||
"recentResultsPing": "Ping",
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"ping": "Ping",
|
||||
"status": "Status",
|
||||
"avgDownload": "Gns Download",
|
||||
"avgUpload": "Gns Upload",
|
||||
"avgPing": "Gns Ping",
|
||||
"healthy": "Sund",
|
||||
"unhealthy": "Usund",
|
||||
"ok": "OK",
|
||||
"fail": "Fejl",
|
||||
"noSectionsEnabled": "Aktivér mindst én sektion i widget-indstillinger.",
|
||||
"error": {
|
||||
"internalServerError": "Kunne ikke hente hastighedstestdata"
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "Systemressourcer",
|
||||
"description": "CPU, Hukommelse, Disk og andet hardwareforbrug af dit system",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "Oversættere",
|
||||
"subtitle": "{count} bidragende oversættere på mange sprog"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "Biblioteker",
|
||||
"subtitle": "{count} brugt i koden for Homarr"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "",
|
||||
"description": "Einbetten von Inhalten aus dem Internet. Einige Websites können den Zugriff einschränken.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "Übersetzer",
|
||||
"subtitle": "{count} tragen Übersetzungen in viele Sprachen bei"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "Bibliotheken",
|
||||
"subtitle": "{count} im Code von Homarr verwendet"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "Einbetten von Inhalten aus dem Internet. Einige Websites können den Zugriff einschränken.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "Beschreibung ausblenden"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "Keine Benachrichtigungen zum Anzeigen.",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "Systemressourcen",
|
||||
"description": "CPU, Arbeitsspeicher, Festplatte und andere Hardware-Nutzung Ihres Systems",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "Übersetzer",
|
||||
"subtitle": "{count} tragen Übersetzungen in viele Sprachen bei"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "Bibliotheken",
|
||||
"subtitle": "{count} im Code von Homarr verwendet"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iframe",
|
||||
"description": "Ενσωματώστε οποιοδήποτε περιεχόμενο από το διαδίκτυο. Ορισμένοι ιστότοποι ενδέχεται να περιορίζουν την πρόσβαση.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "Anchor Note",
|
||||
"description": "Displays a selected note from Anchor",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": "Note"
|
||||
},
|
||||
"showTitle": {
|
||||
"label": "Show title"
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": "Show last updated"
|
||||
}
|
||||
},
|
||||
"empty": "Select a note to display",
|
||||
"emptyContent": "This note has no content",
|
||||
"notFound": "Note not found",
|
||||
"noNotes": "No notes found",
|
||||
"integrationRequired": "Select an Anchor integration first",
|
||||
"updatedAt": "Updated {date}",
|
||||
"untitled": "Untitled",
|
||||
"edit": "Edit",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"readOnlyViewer": "You have viewer access to this note. Editing is disabled.",
|
||||
"saveForbidden": "You do not have permission to edit this note.",
|
||||
"saveFailed": "Failed to save this note. Please try again."
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "Embed any content from the Internet. Some websites may restrict access.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
|
||||
@@ -3790,10 +3790,6 @@
|
||||
"title": "Translators",
|
||||
"subtitle": "{count} contributing translations in many languages"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "Sponsors",
|
||||
"subtitle": "{count} supporting the project financially"
|
||||
},
|
||||
"libraries": {
|
||||
"title": "Libraries",
|
||||
"subtitle": "{count} used in the Code of Homarr"
|
||||
|
||||
@@ -1526,13 +1526,13 @@
|
||||
}
|
||||
},
|
||||
"timetable": {
|
||||
"name": "Horarios de estación",
|
||||
"description": "Muestra los horarios de salida de una estación",
|
||||
"title": "Salidas {station}",
|
||||
"noStation": "No se ha seleccionado ninguna integración",
|
||||
"name": "",
|
||||
"description": "",
|
||||
"title": "",
|
||||
"noStation": "",
|
||||
"option": {
|
||||
"station": {
|
||||
"label": "Estación"
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "Nota de Anchor",
|
||||
"description": "Muestra una nota seleccionada de Anchor",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": "Nota"
|
||||
},
|
||||
"showTitle": {
|
||||
"label": "Mostrar título"
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": "Mostrar última actualización"
|
||||
}
|
||||
},
|
||||
"empty": "Selecciona una nota para mostrar",
|
||||
"emptyContent": "Esta nota no tiene contenido",
|
||||
"notFound": "No se encontró la nota",
|
||||
"noNotes": "No se encontraron notas",
|
||||
"integrationRequired": "Selecciona primero una integración de Anchor",
|
||||
"updatedAt": "Actualizado a {date}",
|
||||
"untitled": "Sin título",
|
||||
"edit": "Editar",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"readOnlyViewer": "Solo tienes acceso de lectura para esta nota.",
|
||||
"saveForbidden": "No tienes permiso para editar esta nota.",
|
||||
"saveFailed": "Error al guardar esta nota. Por favor, inténtalo de nuevo."
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "Incrusta cualquier contenido de Internet. Algunos sitios web pueden restringir el acceso.",
|
||||
@@ -1666,7 +1638,7 @@
|
||||
"label": "Permitir geolocalización"
|
||||
},
|
||||
"allowModals": {
|
||||
"label": "Permitir modales"
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -1901,7 +1873,7 @@
|
||||
"label": "Mostrar información de la memoria"
|
||||
},
|
||||
"gpu": {
|
||||
"label": "Mostrar Información de GPU"
|
||||
"label": ""
|
||||
},
|
||||
"showUptime": {
|
||||
"label": "Mostrar tiempo de actividad"
|
||||
@@ -2042,58 +2014,58 @@
|
||||
}
|
||||
},
|
||||
"tracearr": {
|
||||
"name": "Tracearr",
|
||||
"description": "Monitoreo de servidores multimedia mejorado con información detallada de flujo y transcodificación",
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showStreams": {
|
||||
"label": "Mostrar flujos activos"
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": "Mostrar resumen de estadísticas"
|
||||
"label": ""
|
||||
},
|
||||
"showRecentActivity": {
|
||||
"label": "Mostrar actividad reciente"
|
||||
"label": ""
|
||||
},
|
||||
"showViolations": {
|
||||
"label": "Mostrar infracciones"
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"stats": {
|
||||
"activeStreams": "Flujos activos",
|
||||
"totalUsers": "Usuarios totales",
|
||||
"transcodes": "Transcodificaciones",
|
||||
"bandwidth": "Ancho de banda"
|
||||
"activeStreams": "",
|
||||
"totalUsers": "",
|
||||
"transcodes": "",
|
||||
"bandwidth": ""
|
||||
},
|
||||
"streams": {
|
||||
"empty": "No hay flujos activos",
|
||||
"user": "Usuario",
|
||||
"media": "Medio",
|
||||
"state": "Estado",
|
||||
"quality": "Calidad",
|
||||
"device": "Dispositivo",
|
||||
"progress": "Progreso",
|
||||
"empty": "",
|
||||
"user": "",
|
||||
"media": "",
|
||||
"state": "",
|
||||
"quality": "",
|
||||
"device": "",
|
||||
"progress": "",
|
||||
"videoDecision": {
|
||||
"directPlay": "Reproducción directa",
|
||||
"transcode": "Transcodificación",
|
||||
"directStream": "Flujo directo"
|
||||
"directPlay": "",
|
||||
"transcode": "",
|
||||
"directStream": ""
|
||||
}
|
||||
},
|
||||
"recentActivity": {
|
||||
"title": "Actividad reciente",
|
||||
"empty": "Sin actividad reciente",
|
||||
"watched": "Visto",
|
||||
"partial": "Parcial"
|
||||
"title": "",
|
||||
"empty": "",
|
||||
"watched": "",
|
||||
"partial": ""
|
||||
},
|
||||
"violations": {
|
||||
"title": "Infracciones",
|
||||
"empty": "Sin infracciones recientes",
|
||||
"rule": "Regla",
|
||||
"user": "Usuario"
|
||||
"title": "",
|
||||
"empty": "",
|
||||
"rule": "",
|
||||
"user": ""
|
||||
},
|
||||
"noSectionsEnabled": "No hay secciones habilitadas",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"noIntegration": "Ninguna integración de Tracearr configurada",
|
||||
"internalServerError": "Error al obtener los datos de Tracearr"
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -2192,50 +2164,50 @@
|
||||
}
|
||||
},
|
||||
"immich-serverStats": {
|
||||
"name": "Estadísticas del servidor Immich",
|
||||
"description": "Información del servidor Immich",
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showUsers": {
|
||||
"label": "Mostrar usuarios",
|
||||
"description": "Mostrar el número de usuarios del servidor"
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"showPhotos": {
|
||||
"label": "Mostrar fotos",
|
||||
"description": "Mostrar el número de fotos del servidor"
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"showVideos": {
|
||||
"label": "Mostrar vídeos",
|
||||
"description": "Mostrar el número de vídeos del servidor"
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"showStorage": {
|
||||
"label": "Mostrar almacenamiento",
|
||||
"description": "Mostrar la cantidad total de almacenamiento usado"
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"users": "Usuarios",
|
||||
"photos": "Fotos",
|
||||
"videos": "Vídeos",
|
||||
"storage": "Almacenamiento usado"
|
||||
"users": "",
|
||||
"photos": "",
|
||||
"videos": "",
|
||||
"storage": ""
|
||||
},
|
||||
"immich-albumCarousel": {
|
||||
"name": "Álbum de Immich",
|
||||
"name": "",
|
||||
"option": {
|
||||
"albumId": {
|
||||
"label": "ID de álbum",
|
||||
"description": "Este es un identificador único que se refiere a un álbum de Immich. Puedes encontrarlo abriendo un álbum y copiando el ID que se muestra en la URL. Como por ejemplo: 'a21aca09-9f05-47dc-b1a1-7b53c547226e'"
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"rotationIntervalSeconds": {
|
||||
"label": "Intervalo de actualización en segundos",
|
||||
"description": "Intervalo de actualización en segundos entre las fotos del álbum. El rango debe estar entre 1 y 3600 segundos, ambos incluidos."
|
||||
"label": "",
|
||||
"description": ""
|
||||
},
|
||||
"showPhotoInfo": {
|
||||
"label": "Mostrar información de la foto",
|
||||
"description": "Mostrar la fecha y el índice actual de la foto en la parte inferior de la presentación"
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"description": "Presentación de fotos de un álbum de Immich",
|
||||
"noAlbumSelected": "No se ha seleccionado ningún álbum. Edita el widget y selecciona uno.",
|
||||
"noPhotos": "No hay fotos en este álbum"
|
||||
"description": "",
|
||||
"noAlbumSelected": "",
|
||||
"noPhotos": ""
|
||||
},
|
||||
"downloads": {
|
||||
"name": "Cliente de descarga",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "Ocultar descripción"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": "Mostrar imagen del póster"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "Ninguna notificación para mostrar.",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "Speedtest Tracker",
|
||||
"description": "Muestra los resultados de tu instancia de Speedtest Tracker.",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": "Mostrar último resultado"
|
||||
},
|
||||
"showStats": {
|
||||
"label": "Mostrar promedios"
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": "Mostrar resultados recientes"
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": "Mostrar gráfico de latencia"
|
||||
}
|
||||
},
|
||||
"latestResult": "Último resultado",
|
||||
"averages": "Promedios",
|
||||
"tests": "{count} pruebas",
|
||||
"recentResults": "Resultados recientes",
|
||||
"recentResultsSpeed": "Velocidad",
|
||||
"recentResultsPing": "Latencia",
|
||||
"download": "Descarga",
|
||||
"upload": "Subida",
|
||||
"ping": "Latencia",
|
||||
"status": "Estado",
|
||||
"avgDownload": "Descarga promedio",
|
||||
"avgUpload": "Subida promedio",
|
||||
"avgPing": "Latencia promedio",
|
||||
"healthy": "Buen estado",
|
||||
"unhealthy": "Mal estado",
|
||||
"ok": "Correcto",
|
||||
"fail": "Fallido",
|
||||
"noSectionsEnabled": "Habilitar al menos una sección en las opciones del widget.",
|
||||
"error": {
|
||||
"internalServerError": "Error al obtener los datos del test de velocidad"
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "Recursos del sistema",
|
||||
"description": "CPU, Memoria, Disco y otro uso de hardware de su sistema",
|
||||
@@ -2812,7 +2742,7 @@
|
||||
"option": {
|
||||
"cpu": "CPU",
|
||||
"memory": "Memoria",
|
||||
"gpu": "GPU",
|
||||
"gpu": "",
|
||||
"network": "Red"
|
||||
}
|
||||
},
|
||||
@@ -2829,7 +2759,7 @@
|
||||
"card": {
|
||||
"cpu": "CPU",
|
||||
"memory": "MEM",
|
||||
"gpu": "GPU",
|
||||
"gpu": "",
|
||||
"network": "RED",
|
||||
"up": "ENCENDIDO",
|
||||
"down": "APAGADO"
|
||||
@@ -3595,7 +3525,7 @@
|
||||
"label": "Estado del Servidor de Minecraft"
|
||||
},
|
||||
"timetable": {
|
||||
"label": "Horarios de estación"
|
||||
"label": ""
|
||||
},
|
||||
"iconsUpdater": {
|
||||
"label": "Actualizador de iconos"
|
||||
@@ -3667,7 +3597,7 @@
|
||||
"label": "Tiempo"
|
||||
},
|
||||
"tracearr": {
|
||||
"label": "Tracearr"
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"interval": {
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "Traductores",
|
||||
"subtitle": "{count} contribuyendo con las traducciones en muchos idiomas"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "Librerías",
|
||||
"subtitle": "{count} usadas en el Código de Homarr"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "Intégrer n'importe quel contenu à partir d'Internet. Certains sites Web peuvent restreindre l'accès.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "Masquer la description"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "Aucune notification à afficher.",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "Ressources du système",
|
||||
"description": "CPU, Mémoire, Disque et autre utilisation matérielle de votre système",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "Traducteurs",
|
||||
"subtitle": "{count} ont contribué à des traductions dans de nombreuses langues"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "Bibliothèques",
|
||||
"subtitle": "{count} utilisés dans le Code d'Homarr"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "iFrame",
|
||||
"description": "הטמע כל תוכן מהאינטרנט. חלק מהאתרים עשויים להגביל את הגישה.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": "הסתר תיאור"
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "אין התראות להצגה.",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "משאבי מערכת",
|
||||
"description": "שימוש במעבד, זיכרון, דיסק וחומרה אחרת של המערכת שלך",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "מתרגמים",
|
||||
"subtitle": "{count} תורמים בתרגום לשפות שונות"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "ספריות",
|
||||
"subtitle": "{count} בשימוש בקוד של Homarr"
|
||||
|
||||
@@ -1606,34 +1606,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"anchorNote": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"noteId": {
|
||||
"label": ""
|
||||
},
|
||||
"showTitle": {
|
||||
"label": ""
|
||||
},
|
||||
"showUpdatedAt": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": "",
|
||||
"emptyContent": "",
|
||||
"notFound": "",
|
||||
"noNotes": "",
|
||||
"integrationRequired": "",
|
||||
"updatedAt": "",
|
||||
"untitled": "",
|
||||
"edit": "",
|
||||
"save": "",
|
||||
"cancel": "",
|
||||
"readOnlyViewer": "",
|
||||
"saveForbidden": "",
|
||||
"saveFailed": ""
|
||||
},
|
||||
"iframe": {
|
||||
"name": "",
|
||||
"description": "Ugradite bilo koji sadržaj s interneta. Neke web stranice mogu ograničiti pristup.",
|
||||
@@ -2544,9 +2516,6 @@
|
||||
},
|
||||
"hideDescription": {
|
||||
"label": ""
|
||||
},
|
||||
"showPosterImage": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2760,45 +2729,6 @@
|
||||
"noItems": "",
|
||||
"option": {}
|
||||
},
|
||||
"speedtestTracker": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showLatestResult": {
|
||||
"label": ""
|
||||
},
|
||||
"showStats": {
|
||||
"label": ""
|
||||
},
|
||||
"showRecentResults": {
|
||||
"label": ""
|
||||
},
|
||||
"showPingGraph": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"latestResult": "",
|
||||
"averages": "",
|
||||
"tests": "",
|
||||
"recentResults": "",
|
||||
"recentResultsSpeed": "",
|
||||
"recentResultsPing": "",
|
||||
"download": "",
|
||||
"upload": "",
|
||||
"ping": "",
|
||||
"status": "",
|
||||
"avgDownload": "",
|
||||
"avgUpload": "",
|
||||
"avgPing": "",
|
||||
"healthy": "",
|
||||
"unhealthy": "",
|
||||
"ok": "",
|
||||
"fail": "",
|
||||
"noSectionsEnabled": "",
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"systemResources": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
@@ -3790,10 +3720,6 @@
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
},
|
||||
"libraries": {
|
||||
"title": "",
|
||||
"subtitle": ""
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user