diff --git a/.env.example b/.env.example
index d643c1194..ff3c38f3d 100644
--- a/.env.example
+++ b/.env.example
@@ -12,10 +12,6 @@ AUTH_SECRET="supersecret"
# or starting the project without any (which will show a randomly generated one).
SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
-# Cron job API key is generated every time the container starts as it is required for communication between nextjs-api and tasks-api
-# export CRON_JOB_API_KEY=$(openssl rand -base64 32)
-# CRON_JOB_API_KEY="your-generated-api-key"
-
LOG_LEVEL='info'
# This is how you can use the sqlite driver:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index e2d521e8f..d824200e4 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -33,6 +33,7 @@ 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
diff --git a/.github/ISSUE_TEMPLATE/integration.yml b/.github/ISSUE_TEMPLATE/integration.yml
index 3aeb492e7..549808df3 100644
--- a/.github/ISSUE_TEMPLATE/integration.yml
+++ b/.github/ISSUE_TEMPLATE/integration.yml
@@ -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
diff --git a/.github/ISSUE_TEMPLATE/widget.yml b/.github/ISSUE_TEMPLATE/widget.yml
index 9ede8d0ba..aa1d0f6a1 100644
--- a/.github/ISSUE_TEMPLATE/widget.yml
+++ b/.github/ISSUE_TEMPLATE/widget.yml
@@ -24,7 +24,7 @@ body:
attributes:
label: Are you willing to contribute this yourself?
options:
- - Yes
- - No
+ - "Yes"
+ - "No"
validations:
required: true
diff --git a/.github/workflows/automatic-approval.yml b/.github/workflows/automatic-approval.yml
index 1fb1d682f..6647bad82 100644
--- a/.github/workflows/automatic-approval.yml
+++ b/.github/workflows/automatic-approval.yml
@@ -23,7 +23,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
- app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
+ client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
permission-pull-requests: write # required to approve pull request
- name: Install GitHub CLI
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index a61ef630a..5dc6df825 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -11,14 +11,10 @@ on:
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
- cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
-# 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:
@@ -59,7 +55,7 @@ jobs:
uses: ./tooling/github/setup
- name: Typecheck
- run: turbo typecheck
+ run: pnpm typecheck
test:
runs-on: ubuntu-latest
diff --git a/.github/workflows/crowdin-schedule-download.yml b/.github/workflows/crowdin-schedule-download.yml
index 4a176ca0f..a5dec8869 100644
--- a/.github/workflows/crowdin-schedule-download.yml
+++ b/.github/workflows/crowdin-schedule-download.yml
@@ -22,7 +22,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
- app-id: ${{ secrets.CROWDIN_APP_ID }}
+ client-id: ${{ secrets.CROWDIN_APP_ID }}
permission-contents: write # required to commit to crowdin branch
permission-pull-requests: write # required to create pull request
diff --git a/.github/workflows/deployment-beta-release.yml b/.github/workflows/deployment-beta-release.yml
new file mode 100644
index 000000000..fd79d1502
--- /dev/null
+++ b/.github/workflows/deployment-beta-release.yml
@@ -0,0 +1,104 @@
+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 }}``
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"
diff --git a/.github/workflows/deployment-docker-image.yml b/.github/workflows/deployment-docker-image.yml
index cec31c489..9dea69600 100644
--- a/.github/workflows/deployment-docker-image.yml
+++ b/.github/workflows/deployment-docker-image.yml
@@ -5,6 +5,7 @@ on:
branches:
- main
- beta
+ - next
- dev
workflow_dispatch:
inputs:
@@ -34,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
env:
- SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' }}
+ SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' || github.ref_name == 'next' }}
outputs:
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
@@ -53,8 +54,8 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
- 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
+ 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
- uses: actions/checkout@v6
if: env.SKIP_RELEASE == 'false'
@@ -95,20 +96,21 @@ jobs:
id: read-git-ref
run: |
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- - name: Update dev branch
+ - name: Update dev / next 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 dev
- git checkout dev
- git pull origin dev
+ git fetch origin ${{ env.UPSTREAM_BRANCH }}
+ git checkout ${{ env.UPSTREAM_BRANCH }}
+ git pull origin ${{ env.UPSTREAM_BRANCH }}
git rebase ${{ github.ref_name }}
- git push origin dev
+ git push origin ${{ env.UPSTREAM_BRANCH }}
build-amd64:
name: Build docker image for amd64
needs: release
@@ -234,7 +236,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
- app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
+ client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
permission-contents: write # required to publish the release
- name: Log in to the Container registry
@@ -274,6 +276,6 @@ jobs:
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- uses: Ilshidur/action-discord@master
+ uses: Ilshidur/action-discord@0.4.0
with:
args: "Successfully deployed images for branch **${{ github.ref_name }}**. Tagged as **${{env.NEXT_VERSION}}**."
diff --git a/.github/workflows/deployment-weekly-release.yml b/.github/workflows/deployment-weekly-release.yml
index 37d0c4364..8cb117b8c 100644
--- a/.github/workflows/deployment-weekly-release.yml
+++ b/.github/workflows/deployment-weekly-release.yml
@@ -21,10 +21,10 @@ jobs:
timeout-minutes: 2
steps:
- name: Discord notification
- if: ${{ github.events.inputs.send-notifications }}
+ if: ${{ github.event.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- uses: Ilshidur/action-discord@master
+ uses: Ilshidur/action-discord@0.4.0
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,14 +38,15 @@ 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**.
Manual action may be required for major bumps.
Detected change to be ``${{ steps.semver.outputs.bump }}``
Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``" --base main --head dev --label automerge'
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Discord notification
- if: ${{ github.events.inputs.send-notifications }}
+ if: ${{ github.event.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- uses: Ilshidur/action-discord@master
+ 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 }} (new behaviour: ${{ steps.semver.outputs.bump }})"
- name: Obtain token
@@ -53,7 +54,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
- app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
+ client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
permission-pull-requests: write
- name: Approve PR
env:
@@ -65,7 +66,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
- app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
+ client-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
@@ -74,7 +75,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 master
+ MERGE_METHOD: merge # we prefer merge commits for merging to main
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 }}"
@@ -83,23 +84,23 @@ jobs:
MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
- name: Merged Discord notification
- if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.events.inputs.send-notifications }}
+ if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.event.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- uses: Ilshidur/action-discord@master
+ uses: Ilshidur/action-discord@0.4.0
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.events.inputs.send-notifications }}
+ if: ${{ steps.semver.outputs.bump == 'major' && github.event.inputs.send-notifications }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- uses: Ilshidur/action-discord@master
+ 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 }}"
- name: Discord Fail Notification
- if: failure() && github.events.inputs.send-notifications
+ if: failure() && github.event.inputs.send-notifications
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- uses: Ilshidur/action-discord@master
+ uses: Ilshidur/action-discord@0.4.0
with:
args: "The automatic release workflow [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) has failed"
diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml
index f667d2669..514f225b4 100644
--- a/.github/workflows/on-release.yml
+++ b/.github/workflows/on-release.yml
@@ -15,7 +15,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
- app-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
+ client-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 }}
- app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
+ client-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
diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml
index 7fb73652c..925e490c7 100644
--- a/.github/workflows/update-contributors.yml
+++ b/.github/workflows/update-contributors.yml
@@ -24,7 +24,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
- app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
+ client-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
diff --git a/.github/workflows/update-integration-list.yml b/.github/workflows/update-integration-list.yml
index 204246d7b..05bb05d27 100644
--- a/.github/workflows/update-integration-list.yml
+++ b/.github/workflows/update-integration-list.yml
@@ -24,7 +24,7 @@ jobs:
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
- app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
+ client-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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c9aae9c1e..94131c6d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,33 @@
+## [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
diff --git a/Dockerfile b/Dockerfile
index eab3c4419..51b562f7d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -43,8 +43,6 @@ 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
diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index 92538095f..4a7323606 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -44,9 +44,11 @@
"@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:",
diff --git a/apps/nextjs/src/app/[locale]/manage/about/page.tsx b/apps/nextjs/src/app/[locale]/manage/about/page.tsx
index 9aa4aeb10..0be4d5687 100644
--- a/apps/nextjs/src/app/[locale]/manage/about/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/about/page.tsx
@@ -25,7 +25,14 @@ import {
Title,
UnstyledButton,
} from "@mantine/core";
-import { IconKeyboard, IconLanguage, IconLibrary, IconPackage, IconUsers } from "@tabler/icons-react";
+import {
+ IconKeyboard,
+ IconLanguage,
+ IconLibrary,
+ IconMoneybagHeart,
+ IconPackage,
+ IconUsers,
+} from "@tabler/icons-react";
import { capitalize, objectEntries } from "@homarr/common";
import { hotkeys } from "@homarr/definitions";
@@ -37,6 +44,7 @@ import { createMetaTitle } from "~/metadata";
import type { PackageJsonDependencies } from "~/versions/package-reader";
import { getPackageVersion } 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";
@@ -71,6 +79,10 @@ 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 (
@@ -136,6 +148,30 @@ export default async function AboutPage() {
+
+ }>
+
+ {t("accordion.sponsors.title")}
+
+ {t("accordion.sponsors.subtitle", {
+ count: String(openCollectiveContributors.length),
+ })}
+
+
+
+
+
+ {openCollectiveContributors.map((sponsor) => (
+
+ ))}
+
+
+
}>
diff --git a/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx
index e848c0fcf..f3fe3b825 100644
--- a/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx
@@ -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";
+import { openApiDocument } from "@homarr/api/open-api";
import { api } from "@homarr/api/server";
import { auth } from "@homarr/auth/next";
import { extractBaseUrlFromHeaders } from "@homarr/common";
diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_first-day-of-week.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_first-day-of-week.tsx
index e2b1a859d..71bef0737 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_first-day-of-week.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_first-day-of-week.tsx
@@ -30,7 +30,7 @@ export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
},
onSuccess(_, variables) {
form.setInitialValues({
- firstDayOfWeek: variables.firstDayOfWeek,
+ firstDayOfWeek: variables.firstDayOfWeek as DayOfWeek,
});
showSuccessNotification({
message: t("user.action.changeFirstDayOfWeek.notification.success.message"),
diff --git a/apps/nextjs/src/app/api/[...trpc]/route.ts b/apps/nextjs/src/app/api/[...trpc]/route.ts
index 2980900a8..1ae1e5e92 100644
--- a/apps/nextjs/src/app/api/[...trpc]/route.ts
+++ b/apps/nextjs/src/app/api/[...trpc]/route.ts
@@ -2,7 +2,8 @@ import type { NextRequest } from "next/server";
import { userAgent } from "next/server";
import { createOpenApiFetchHandler } from "trpc-to-openapi";
-import { appRouter, createTRPCContext } from "@homarr/api";
+import { createTRPCContext } from "@homarr/api";
+import { openApiRouter } from "@homarr/api/open-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";
@@ -30,7 +31,7 @@ const handlerAsync = async (req: NextRequest) => {
return createOpenApiFetchHandler({
req,
endpoint: "/",
- router: appRouter,
+ router: openApiRouter,
createContext: () => createTRPCContext({ session, headers: req.headers }),
onError({ error, path, type }) {
logger.error(new ErrorWithMetadata("tRPC Error occured", { path, type }, { cause: error }));
diff --git a/apps/nextjs/src/app/api/about/contributors/opencollective/route.ts b/apps/nextjs/src/app/api/about/contributors/opencollective/route.ts
new file mode 100644
index 000000000..369f14a8c
--- /dev/null
+++ b/apps/nextjs/src/app/api/about/contributors/opencollective/route.ts
@@ -0,0 +1,9 @@
+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";
diff --git a/apps/nextjs/src/app/api/openapi/route.ts b/apps/nextjs/src/app/api/openapi/route.ts
index d4c514b68..26f26f9e7 100644
--- a/apps/nextjs/src/app/api/openapi/route.ts
+++ b/apps/nextjs/src/app/api/openapi/route.ts
@@ -1,6 +1,6 @@
import { NextResponse } from "next/server";
-import { openApiDocument } from "@homarr/api";
+import { openApiDocument } from "@homarr/api/open-api";
import { extractBaseUrlFromHeaders } from "@homarr/common";
export function GET(request: Request) {
diff --git a/apps/nextjs/src/instrumentation.ts b/apps/nextjs/src/instrumentation.ts
new file mode 100644
index 000000000..19a2865b7
--- /dev/null
+++ b/apps/nextjs/src/instrumentation.ts
@@ -0,0 +1,6 @@
+export async function register() {
+ if (process.env.NEXT_RUNTIME === "nodejs") {
+ await import("@homarr/tasks");
+ await import("@homarr/websocket");
+ }
+}
diff --git a/apps/tasks/package.json b/apps/tasks/package.json
index 3cb918df5..28e34be63 100644
--- a/apps/tasks/package.json
+++ b/apps/tasks/package.json
@@ -5,12 +5,11 @@
"license": "Apache-2.0",
"type": "module",
"exports": {
- ".": "./src/index.ts"
+ ".": "./src/main.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",
@@ -24,7 +23,6 @@
"@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",
@@ -37,6 +35,7 @@
"@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:",
diff --git a/apps/tasks/src/main.ts b/apps/tasks/src/main.ts
index 4c60f1646..55416e689 100644
--- a/apps/tasks/src/main.ts
+++ b/apps/tasks/src/main.ts
@@ -1,52 +1,12 @@
// 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 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["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);
- }
})();
diff --git a/apps/websocket/package.json b/apps/websocket/package.json
index 8fe731bcb..c4a9cf9d0 100644
--- a/apps/websocket/package.json
+++ b/apps/websocket/package.json
@@ -4,10 +4,12 @@
"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",
diff --git a/deployments/prebuilt-debian/package.json b/deployments/prebuilt-debian/package.json
index 84ee59c0d..15876ecc1 100644
--- a/deployments/prebuilt-debian/package.json
+++ b/deployments/prebuilt-debian/package.json
@@ -4,10 +4,10 @@
"dependencies": {
"better-sqlite3": "^12.9.0"
},
- "packageManager": "pnpm@10.33.0",
+ "packageManager": "pnpm@10.33.2",
"engines": {
"node": ">=24.15.0",
- "pnpm": ">=10.33.0"
+ "pnpm": ">=10.33.2"
},
"pnpm": {
"onlyBuiltDependencies": [
diff --git a/package.json b/package.json
index cf499a5af..5f0eed5d9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "homarr",
- "version": "1.59.3",
+ "version": "1.60.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": "turbo dev --parallel",
+ "dev": "pnpm -F nextjs dev",
"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",
@@ -59,10 +59,10 @@
"vite-tsconfig-paths": "catalog:",
"vitest": "catalog:"
},
- "packageManager": "pnpm@10.33.0",
+ "packageManager": "pnpm@10.33.2",
"engines": {
"node": ">=24.15.0",
- "pnpm": ">=10.33.0"
+ "pnpm": ">=10.33.2"
},
"pnpm": {
"onlyBuiltDependencies": [
@@ -79,12 +79,12 @@
"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.1",
+ "axios@>=1.0.0 <1.8.2": ">=1.15.2",
"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.14",
+ "hono@<4.6.5": ">=4.12.15",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.9",
"prismjs@<1.30.0": ">=1.30.0",
@@ -95,7 +95,7 @@
"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.8"
+ "vite@>=5.0.0 <=5.4.18": ">=8.0.10"
},
"patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch",
diff --git a/packages/api/package.json b/packages/api/package.json
index 04f7bae84..1c137c154 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -9,7 +9,8 @@
"./client": "./src/client.ts",
"./server": "./src/server.ts",
"./websocket": "./src/websocket.ts",
- "./shared": "./src/shared.ts"
+ "./shared": "./src/shared.ts",
+ "./open-api": "./src/open-api.ts"
},
"main": "./index.ts",
"types": "./index.ts",
@@ -24,7 +25,6 @@
"@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",
diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts
index d339d177f..1cbe6fdd7 100644
--- a/packages/api/src/index.ts
+++ b/packages/api/src/index.ts
@@ -1,6 +1,5 @@
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";
@@ -30,5 +29,5 @@ type RouterInputs = inferRouterInputs;
**/
type RouterOutputs = inferRouterOutputs;
-export { createTRPCContext, appRouter, createCaller, openApiDocument };
+export { createTRPCContext, appRouter, createCaller };
export type { AppRouter, RouterInputs, RouterOutputs };
diff --git a/packages/api/src/open-api.ts b/packages/api/src/open-api.ts
index b4827871c..d789582df 100644
--- a/packages/api/src/open-api.ts
+++ b/packages/api/src/open-api.ts
@@ -2,10 +2,21 @@ import { generateOpenApiDocument } from "trpc-to-openapi";
import { API_KEY_HEADER_NAME } from "@homarr/auth/api-key";
-import { appRouter } from "./root";
+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,
+});
export const openApiDocument = (base: string) =>
- generateOpenApiDocument(appRouter, {
+ generateOpenApiDocument(openApiRouter, {
title: "Homarr API documentation",
version: "1.0.0",
baseUrl: base,
diff --git a/packages/api/src/router/cron-jobs.ts b/packages/api/src/router/cron-jobs.ts
index 5428ac374..05611191b 100644
--- a/packages/api/src/router/cron-jobs.ts
+++ b/packages/api/src/router/cron-jobs.ts
@@ -2,59 +2,62 @@ 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 }) => {
- await cronJobApi.trigger.mutate(input);
+ .mutation(async ({ input, ctx }) => {
+ await new JobManager(ctx.db, jobGroup).triggerAsync(input);
}),
startJob: permissionRequiredProcedure
.requiresPermission("admin")
.input(jobNameSchema)
- .mutation(async ({ input }) => {
- await cronJobApi.start.mutate(input);
+ .mutation(async ({ input, ctx }) => {
+ await new JobManager(ctx.db, jobGroup).startAsync(input);
}),
stopJob: permissionRequiredProcedure
.requiresPermission("admin")
.input(jobNameSchema)
- .mutation(async ({ input }) => {
- await cronJobApi.stop.mutate(input);
+ .mutation(({ input, ctx }) => {
+ new JobManager(ctx.db, jobGroup).stop(input);
}),
updateJobInterval: permissionRequiredProcedure
.requiresPermission("admin")
.input(
z.object({
name: jobNameSchema,
- cron: cronExpressionSchema,
+ cron: z.string(),
}),
)
- .mutation(async ({ input }) => {
- await cronJobApi.updateInterval.mutate(input);
+ .mutation(async ({ input, ctx }) => {
+ await new JobManager(ctx.db, jobGroup).updateIntervalAsync(input.name, input.cron);
}),
disableJob: permissionRequiredProcedure
.requiresPermission("admin")
.input(jobNameSchema)
- .mutation(async ({ input }) => {
- await cronJobApi.disable.mutate(input);
+ .mutation(async ({ input, ctx }) => {
+ await new JobManager(ctx.db, jobGroup).disableAsync(input);
}),
enableJob: permissionRequiredProcedure
.requiresPermission("admin")
.input(jobNameSchema)
- .mutation(async ({ input }) => {
- await cronJobApi.enable.mutate(input);
+ .mutation(async ({ input, ctx }) => {
+ await new JobManager(ctx.db, jobGroup).enableAsync(input);
}),
- getJobs: permissionRequiredProcedure.requiresPermission("admin").query(async () => {
- return await cronJobApi.getAll.query();
+ getJobs: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => {
+ return await new JobManager(ctx.db, jobGroup).getAllAsync();
}),
subscribeToStatusUpdates: permissionRequiredProcedure.requiresPermission("admin").subscription(() => {
return observable((emit) => {
diff --git a/packages/common/src/stopwatch.ts b/packages/common/src/stopwatch.ts
index 33693631c..c58697083 100644
--- a/packages/common/src/stopwatch.ts
+++ b/packages/common/src/stopwatch.ts
@@ -41,6 +41,10 @@ export class Stopwatch {
return dayjs().millisecond(this.startTime).fromNow(true);
}
+ getElapsedInFormattedMilliseconds() {
+ return `${this.getElapsedInMilliseconds().toFixed(0)}ms`;
+ }
+
getElapsedInMilliseconds() {
return performance.now() - this.startTime;
}
diff --git a/packages/cron-job-api/eslint.config.js b/packages/cron-job-api/eslint.config.js
deleted file mode 100644
index 5b19b6f8a..000000000
--- a/packages/cron-job-api/eslint.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import baseConfig from "@homarr/eslint-config/base";
-
-/** @type {import('typescript-eslint').Config} */
-export default [
- {
- ignores: [],
- },
- ...baseConfig,
-];
diff --git a/packages/cron-job-api/package.json b/packages/cron-job-api/package.json
deleted file mode 100644
index 09bde876b..000000000
--- a/packages/cron-job-api/package.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "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": "catalog:",
- "@trpc/client": "catalog:",
- "@trpc/server": "catalog:",
- "@trpc/tanstack-react-query": "catalog:",
- "node-cron": "catalog:",
- "react": "catalog:",
- "zod": "catalog:"
- },
- "devDependencies": {
- "@homarr/eslint-config": "workspace:^0.2.0",
- "@homarr/prettier-config": "workspace:^0.1.0",
- "@homarr/tsconfig": "workspace:^0.1.0",
- "@types/node-cron": "catalog:",
- "@types/react": "catalog:",
- "eslint": "catalog:",
- "typescript": "catalog:"
- }
-}
diff --git a/packages/cron-job-api/src/client.ts b/packages/cron-job-api/src/client.ts
deleted file mode 100644
index 0d376a328..000000000
--- a/packages/cron-job-api/src/client.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-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({
- 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}`;
-}
diff --git a/packages/cron-job-api/src/constants.ts b/packages/cron-job-api/src/constants.ts
deleted file mode 100644
index 8ce789e72..000000000
--- a/packages/cron-job-api/src/constants.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-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";
diff --git a/packages/cron-job-api/src/env.ts b/packages/cron-job-api/src/env.ts
deleted file mode 100644
index 7efb3f6e2..000000000
--- a/packages/cron-job-api/src/env.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-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,
-});
diff --git a/packages/cron-job-api/src/index.ts b/packages/cron-job-api/src/index.ts
deleted file mode 100644
index 270a51242..000000000
--- a/packages/cron-job-api/src/index.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-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;
- triggerAsync(name: JobGroupKeys): Promise;
- stopAsync(name: JobGroupKeys): Promise;
- updateIntervalAsync(name: JobGroupKeys, cron: string): Promise;
- disableAsync(name: JobGroupKeys): Promise;
- enableAsync(name: JobGroupKeys): Promise;
- 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;
diff --git a/packages/cron-job-api/tsconfig.json b/packages/cron-job-api/tsconfig.json
deleted file mode 100644
index cbe8483d9..000000000
--- a/packages/cron-job-api/tsconfig.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "extends": "@homarr/tsconfig/base.json",
- "compilerOptions": {
- "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
- },
- "include": ["*.ts", "src"],
- "exclude": ["node_modules"]
-}
diff --git a/packages/cron-jobs-core/package.json b/packages/cron-jobs-core/package.json
index 02570d692..71704bdfc 100644
--- a/packages/cron-jobs-core/package.json
+++ b/packages/cron-jobs-core/package.json
@@ -27,13 +27,12 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^",
"@homarr/db": "workspace:^0.1.0",
- "node-cron": "catalog:"
+ "croner": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "@types/node-cron": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
diff --git a/packages/cron-jobs-core/src/creator.ts b/packages/cron-jobs-core/src/creator.ts
index 4f7a9310f..f693a054e 100644
--- a/packages/cron-jobs-core/src/creator.ts
+++ b/packages/cron-jobs-core/src/creator.ts
@@ -1,4 +1,4 @@
-import { createTask, validate } from "node-cron";
+import { Cron } from "croner";
import { Stopwatch } from "@homarr/common";
import type { MaybePromise } from "@homarr/common/types";
@@ -83,10 +83,15 @@ const createCallback = void catchingCallbackAsync(), {
- name,
- timezone: creatorOptions.timezone,
- });
+ const scheduledTask = new Cron(
+ cronExpression,
+ {
+ name,
+ timezone: creatorOptions.timezone,
+ paused: true,
+ },
+ () => void catchingCallbackAsync(),
+ );
creatorOptions.logger.logDebug("The scheduled task for cron job was created", {
name,
cronExpression: defaultCronExpression,
@@ -116,6 +121,7 @@ const createCallback = (
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,
diff --git a/packages/cron-jobs-core/src/group.ts b/packages/cron-jobs-core/src/group.ts
index 77cd6057d..cfb024a9a 100644
--- a/packages/cron-jobs-core/src/group.ts
+++ b/packages/cron-jobs-core/src/group.ts
@@ -1,4 +1,4 @@
-import type { ScheduledTask } from "node-cron";
+import type { Cron } from "croner";
import { objectEntries, objectKeys } from "@homarr/common";
import { db } from "@homarr/db";
@@ -34,7 +34,7 @@ export const createJobGroupCreator = (
});
}
- const tasks = new Map();
+ const tasks = new Map();
return {
initializeAsync: async () => {
@@ -62,7 +62,7 @@ export const createJobGroupCreator = (
name: job.name,
});
await job.onStartAsync();
- await tasks.get(name as string)?.start();
+ tasks.get(name as string)?.resume();
},
startAllAsync: async () => {
for (const job of jobRegistry.values()) {
@@ -74,7 +74,7 @@ export const createJobGroupCreator = (
name: job.name,
});
await job.onStartAsync();
- await tasks.get(job.name)?.start();
+ tasks.get(job.name)?.resume();
}
},
runManuallyAsync: async (name: keyof TJobs) => {
@@ -87,23 +87,23 @@ export const createJobGroupCreator = (
options.logger.logInfo("Running schedule cron job manually.", {
name: job.name,
});
- await tasks.get(name as string)?.execute();
+ await tasks.get(name as string)?.trigger();
},
- stopAsync: async (name: keyof TJobs) => {
+ stop: (name: keyof TJobs) => {
const job = jobRegistry.get(name as string);
if (!job) return;
options.logger.logInfo("Stopping schedule of cron job.", {
name: job.name,
});
- await tasks.get(name as string)?.stop();
+ tasks.get(name as string)?.pause();
},
- stopAllAsync: async () => {
+ stopAll: () => {
for (const job of jobRegistry.values()) {
options.logger.logInfo("Stopping schedule of cron job.", {
name: job.name,
});
- await tasks.get(job.name)?.stop();
+ tasks.get(job.name)?.pause();
}
},
getJobRegistry() {
@@ -112,8 +112,8 @@ export const createJobGroupCreator = (
getTask(name: keyof TJobs) {
return tasks.get(name as string) ?? null;
},
- setTask(name: keyof TJobs, task: ScheduledTask) {
- tasks.set(name as string, task);
+ setTask(name: keyof TJobs, cron: Cron) {
+ tasks.set(name as string, cron);
},
getKeys() {
return objectKeys(jobs);
diff --git a/packages/cron-jobs/package.json b/packages/cron-jobs/package.json
index d0a55d25a..f634d7078 100644
--- a/packages/cron-jobs/package.json
+++ b/packages/cron-jobs/package.json
@@ -5,7 +5,8 @@
"license": "Apache-2.0",
"type": "module",
"exports": {
- ".": "./index.ts"
+ ".": "./index.ts",
+ "./job-manager": "./src/job-manager.ts"
},
"typesVersions": {
"*": {
@@ -38,7 +39,8 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
- "semver-parser": "catalog:"
+ "semver-parser": "catalog:",
+ "zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
diff --git a/packages/cron-jobs/src/index.ts b/packages/cron-jobs/src/index.ts
index 7f2b2349f..0101b08a0 100644
--- a/packages/cron-jobs/src/index.ts
+++ b/packages/cron-jobs/src/index.ts
@@ -27,33 +27,44 @@ import { updateCheckerJob } from "./jobs/update-checker";
import { weatherJob } from "./jobs/weather";
import { createCronJobGroup } from "./lib";
-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,
-});
+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 | undefined;
+}
+
+global.cronJobs ??= getJobGroup();
+
+export const jobGroup = global.cronJobs;
export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number];
diff --git a/apps/tasks/src/job-manager.ts b/packages/cron-jobs/src/job-manager.ts
similarity index 82%
rename from apps/tasks/src/job-manager.ts
rename to packages/cron-jobs/src/job-manager.ts
index f4ec8bf97..82367a8b3 100644
--- a/apps/tasks/src/job-manager.ts
+++ b/packages/cron-jobs/src/job-manager.ts
@@ -1,7 +1,6 @@
-import { schedule, validate as validateCron } from "node-cron";
+import { Cron } from "croner";
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";
@@ -9,7 +8,7 @@ import { cronJobConfigurations } from "@homarr/db/schema";
const logger = createLogger({ module: "jobManager" });
-export class JobManager implements IJobManager {
+export class JobManager {
constructor(
private db: Database,
private jobGroup: typeof cronJobGroup,
@@ -21,28 +20,31 @@ export class JobManager implements IJobManager {
public async triggerAsync(name: JobGroupKeys): Promise {
await this.jobGroup.runManuallyAsync(name);
}
- public async stopAsync(name: JobGroupKeys): Promise {
- await this.jobGroup.stopAsync(name);
+ public stop(name: JobGroupKeys): void {
+ this.jobGroup.stop(name);
}
public async updateIntervalAsync(name: JobGroupKeys, cron: string): Promise {
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}`);
}
- await this.updateConfigurationAsync(name, { cronExpression: cron });
- await this.jobGroup.getTask(name)?.destroy();
+ const updatedConfig = await this.updateConfigurationAsync(name, { cronExpression: cron });
+ this.jobGroup.getTask(name)?.stop();
this.jobGroup.setTask(
name,
- schedule(cron, () => void job.executeAsync(), {
- name,
- }),
+ new Cron(
+ cron,
+ {
+ name,
+ timezone: job.timezone,
+ paused: !updatedConfig.isEnabled,
+ },
+ () => void job.executeAsync(),
+ ),
);
logger.info("Cron job interval updated", { name, expression: cron });
}
@@ -52,7 +54,7 @@ export class JobManager implements IJobManager {
if (!job) throw new Error(`Job ${name} not found`);
await this.updateConfigurationAsync(name, { isEnabled: false });
- await this.jobGroup.stopAsync(name);
+ this.jobGroup.stop(name);
logger.info("Cron job disabled", { name });
}
public async enableAsync(name: JobGroupKeys): Promise {
@@ -86,21 +88,27 @@ export class JobManager implements IJobManager {
name,
configuration: JSON.stringify(configuration),
});
- return;
+ return {
+ name,
+ cronExpression: configuration.cronExpression ?? existingConfig.cronExpression,
+ isEnabled: configuration.isEnabled ?? existingConfig.isEnabled,
+ };
}
const job = this.jobGroup.getJobRegistry().get(name);
if (!job) throw new Error(`Job ${name} not found`);
- await this.db.insert(cronJobConfigurations).values({
+ const updatedConfig = {
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<
diff --git a/packages/cron-jobs/src/jobs/icons-updater.ts b/packages/cron-jobs/src/jobs/icons-updater.ts
index b48c26ea6..cc33727b8 100644
--- a/packages/cron-jobs/src/jobs/icons-updater.ts
+++ b/packages/cron-jobs/src/jobs/icons-updater.ts
@@ -1,9 +1,12 @@
+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 { db, handleTransactionsAsync, inArray, sql } from "@homarr/db";
+import { and, db, eq, handleTransactionsAsync, inArray, notInArray, or } from "@homarr/db";
import { iconRepositories, icons } from "@homarr/db/schema";
import { fetchIconsAsync } from "@homarr/icons";
@@ -26,7 +29,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
logger.info("Fetched icons from repositories", {
repositoryCount: repositoryIconGroups.length,
iconCount: countIcons,
- duration: stopWatch.getElapsedInHumanWords(),
+ duration: stopWatch.getElapsedInFormattedMilliseconds(),
});
const databaseIconRepositories = await db.query.iconRepositories.findMany({
@@ -35,7 +38,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
},
});
- const skippedChecksums: `${string}.${string}`[] = [];
+ const skippedChecksums = new Map();
let countDeleted = 0;
let countInserted = 0;
@@ -50,6 +53,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
if (!repositoryIconGroup.success) {
continue;
}
+ const localSkippedChecksums: string[] = [];
const repositoryInDb = databaseIconRepositories.find(
(dbIconGroup) => dbIconGroup.slug === repositoryIconGroup.slug,
@@ -66,7 +70,7 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
for (const icon of repositoryIconGroup.icons) {
if (dbIconsInRepository.some((dbIcon) => dbIcon.checksum === icon.checksum)) {
- skippedChecksums.push(`${iconRepositoryId}.${icon.checksum}`);
+ localSkippedChecksums.push(icon.checksum);
continue;
}
@@ -79,38 +83,28 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
});
countInserted++;
}
- }
- const deadIcons = allDbIcons.filter(
- (icon) => !skippedChecksums.includes(`${icon.iconRepositoryId}.${icon.checksum}`),
- );
+ skippedChecksums.set(iconRepositoryId, localSkippedChecksums);
+ }
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) => {
- if (newIconRepositories.length >= 1) {
- await transaction.insert(schema.iconRepositories).values(newIconRepositories);
- }
+ const result = (await transaction.delete(schema.icons).where(deadIconsFilter)) as
+ | MySqlRawQueryResult
+ | QueryResult;
- 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))) {
- 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}`),
- ),
- );
- }
+ countDeleted += Array.isArray(result) ? result[0].affectedRows : (result.rowCount ?? 0);
if (deadIconRepositories.length >= 1) {
await transaction.delete(schema.iconRepositories).where(
@@ -121,33 +115,22 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
);
}
- countDeleted += deadIcons.length;
- });
- },
- handleSync() {
- db.transaction((transaction) => {
if (newIconRepositories.length >= 1) {
- transaction.insert(iconRepositories).values(newIconRepositories).run();
+ await transaction.insert(schema.iconRepositories).values(newIconRepositories);
}
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();
+ await transaction.insert(schema.icons).values(chunck);
}
}
- 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();
- }
+ });
+ },
+ handleSync() {
+ db.transaction((transaction) => {
+ const result = transaction.delete(icons).where(deadIconsFilter).run();
+ countDeleted += result.changes;
if (deadIconRepositories.length >= 1) {
transaction
@@ -161,13 +144,22 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
.run();
}
- countDeleted += deadIcons.length;
+ 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();
+ }
+ }
});
},
});
logger.info("Updated icons in database", {
- duration: stopWatch.getElapsedInHumanWords(),
+ duration: stopWatch.getElapsedInFormattedMilliseconds(),
added: countInserted,
deleted: countDeleted,
});
diff --git a/packages/db/package.json b/packages/db/package.json
index 2dd2b1070..6e6c4851d 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -37,6 +37,8 @@
"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 --"
},
diff --git a/packages/translation/src/lang/ca.json b/packages/translation/src/lang/ca.json
index dc9ff655b..5985ba892 100644
--- a/packages/translation/src/lang/ca.json
+++ b/packages/translation/src/lang/ca.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/cn.json b/packages/translation/src/lang/cn.json
index 20cea1c45..c25440a6b 100644
--- a/packages/translation/src/lang/cn.json
+++ b/packages/translation/src/lang/cn.json
@@ -1614,22 +1614,22 @@
"label": ""
},
"showTitle": {
- "label": ""
+ "label": "显示标题"
},
"showUpdatedAt": {
- "label": ""
+ "label": "显示最近更新"
}
},
- "empty": "",
- "emptyContent": "",
- "notFound": "",
+ "empty": "选择要显示的笔记",
+ "emptyContent": "此笔记没有内容",
+ "notFound": "未找到笔记",
"noNotes": "",
"integrationRequired": "",
- "updatedAt": "",
- "untitled": "",
- "edit": "",
- "save": "",
- "cancel": "",
+ "updatedAt": "更新时间:{date}",
+ "untitled": "未命名",
+ "edit": "编辑",
+ "save": "保存",
+ "cancel": "取消",
"readOnlyViewer": "",
"saveForbidden": "",
"saveFailed": ""
@@ -1901,7 +1901,7 @@
"label": "显示内存信息"
},
"gpu": {
- "label": ""
+ "label": "显示 GPU 信息"
},
"showUptime": {
"label": "显示运行时间"
@@ -2200,15 +2200,15 @@
"description": ""
},
"showPhotos": {
- "label": "",
+ "label": "显示图片",
"description": ""
},
"showVideos": {
- "label": "",
+ "label": "显示视频",
"description": ""
},
"showStorage": {
- "label": "",
+ "label": "显示存储",
"description": ""
}
},
@@ -2229,7 +2229,7 @@
"description": ""
},
"showPhotoInfo": {
- "label": "",
+ "label": "显示图片信息",
"description": ""
}
},
@@ -2783,17 +2783,17 @@
"recentResults": "",
"recentResultsSpeed": "",
"recentResultsPing": "",
- "download": "",
- "upload": "",
- "ping": "",
- "status": "",
- "avgDownload": "",
- "avgUpload": "",
- "avgPing": "",
+ "download": "下载",
+ "upload": "上传",
+ "ping": "Ping",
+ "status": "状态",
+ "avgDownload": "平均下载",
+ "avgUpload": "平均上传",
+ "avgPing": "平均Ping",
"healthy": "",
"unhealthy": "",
"ok": "",
- "fail": "",
+ "fail": "失败",
"noSectionsEnabled": "",
"error": {
"internalServerError": ""
@@ -2812,7 +2812,7 @@
"option": {
"cpu": "CPU",
"memory": "内存",
- "gpu": "",
+ "gpu": "GPU",
"network": "网络"
}
},
@@ -2829,7 +2829,7 @@
"card": {
"cpu": "CPU",
"memory": "内存",
- "gpu": "",
+ "gpu": "GPU",
"network": "网络",
"up": "上传",
"down": "下载"
@@ -3790,6 +3790,10 @@
"title": "翻译人员",
"subtitle": "在许多语言中,有{count}人贡献了翻译"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "相关的库",
"subtitle": "{count} 用于 Homarr 代码中"
diff --git a/packages/translation/src/lang/cr.json b/packages/translation/src/lang/cr.json
index 783850e36..d9e5c0224 100644
--- a/packages/translation/src/lang/cr.json
+++ b/packages/translation/src/lang/cr.json
@@ -3790,6 +3790,10 @@
"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"
diff --git a/packages/translation/src/lang/cs.json b/packages/translation/src/lang/cs.json
index c54e4d2e0..ef47cde8b 100644
--- a/packages/translation/src/lang/cs.json
+++ b/packages/translation/src/lang/cs.json
@@ -3790,6 +3790,10 @@
"title": "Překladatelé",
"subtitle": "{count} přispívá překladem do mnoha jazyků"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/da.json b/packages/translation/src/lang/da.json
index 89ed9cd6d..f73d49a08 100644
--- a/packages/translation/src/lang/da.json
+++ b/packages/translation/src/lang/da.json
@@ -3790,6 +3790,10 @@
"title": "Oversættere",
"subtitle": "{count} bidragende oversættere på mange sprog"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Biblioteker",
"subtitle": "{count} brugt i koden for Homarr"
diff --git a/packages/translation/src/lang/de-CH.json b/packages/translation/src/lang/de-CH.json
index 43911eb43..fa5931482 100644
--- a/packages/translation/src/lang/de-CH.json
+++ b/packages/translation/src/lang/de-CH.json
@@ -3790,6 +3790,10 @@
"title": "Übersetzer",
"subtitle": "{count} tragen Übersetzungen in viele Sprachen bei"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Bibliotheken",
"subtitle": "{count} im Code von Homarr verwendet"
diff --git a/packages/translation/src/lang/de.json b/packages/translation/src/lang/de.json
index 05fdd99e0..d46f12613 100644
--- a/packages/translation/src/lang/de.json
+++ b/packages/translation/src/lang/de.json
@@ -3790,6 +3790,10 @@
"title": "Übersetzer",
"subtitle": "{count} tragen Übersetzungen in viele Sprachen bei"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Bibliotheken",
"subtitle": "{count} im Code von Homarr verwendet"
diff --git a/packages/translation/src/lang/el.json b/packages/translation/src/lang/el.json
index 9458ece35..9a15d93b9 100644
--- a/packages/translation/src/lang/el.json
+++ b/packages/translation/src/lang/el.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/en-gb.json b/packages/translation/src/lang/en-gb.json
index be90863e8..2e276bb37 100644
--- a/packages/translation/src/lang/en-gb.json
+++ b/packages/translation/src/lang/en-gb.json
@@ -1607,32 +1607,32 @@
}
},
"anchorNote": {
- "name": "",
- "description": "",
+ "name": "Anchor Note",
+ "description": "Displays a selected note from Anchor",
"option": {
"noteId": {
- "label": ""
+ "label": "Note"
},
"showTitle": {
- "label": ""
+ "label": "Show title"
},
"showUpdatedAt": {
- "label": ""
+ "label": "Show last updated"
}
},
- "empty": "",
- "emptyContent": "",
- "notFound": "",
- "noNotes": "",
- "integrationRequired": "",
- "updatedAt": "",
- "untitled": "",
- "edit": "",
- "save": "",
- "cancel": "",
- "readOnlyViewer": "",
- "saveForbidden": "",
- "saveFailed": ""
+ "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",
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json
index aadcb0d96..817abb3f8 100644
--- a/packages/translation/src/lang/en.json
+++ b/packages/translation/src/lang/en.json
@@ -3790,6 +3790,10 @@
"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"
diff --git a/packages/translation/src/lang/es.json b/packages/translation/src/lang/es.json
index 71c287439..5ec0b8573 100644
--- a/packages/translation/src/lang/es.json
+++ b/packages/translation/src/lang/es.json
@@ -3790,6 +3790,10 @@
"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"
diff --git a/packages/translation/src/lang/et.json b/packages/translation/src/lang/et.json
index d13dd709a..3f4b529f7 100644
--- a/packages/translation/src/lang/et.json
+++ b/packages/translation/src/lang/et.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/fr.json b/packages/translation/src/lang/fr.json
index c4e356183..aba3a3589 100644
--- a/packages/translation/src/lang/fr.json
+++ b/packages/translation/src/lang/fr.json
@@ -3790,6 +3790,10 @@
"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"
diff --git a/packages/translation/src/lang/he.json b/packages/translation/src/lang/he.json
index dc33eb780..68ff225d1 100644
--- a/packages/translation/src/lang/he.json
+++ b/packages/translation/src/lang/he.json
@@ -3790,6 +3790,10 @@
"title": "מתרגמים",
"subtitle": "{count} תורמים בתרגום לשפות שונות"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "ספריות",
"subtitle": "{count} בשימוש בקוד של Homarr"
diff --git a/packages/translation/src/lang/hr.json b/packages/translation/src/lang/hr.json
index 69e5eee71..c6b65dbbd 100644
--- a/packages/translation/src/lang/hr.json
+++ b/packages/translation/src/lang/hr.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/hu.json b/packages/translation/src/lang/hu.json
index ffdb698d8..44da07150 100644
--- a/packages/translation/src/lang/hu.json
+++ b/packages/translation/src/lang/hu.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/it.json b/packages/translation/src/lang/it.json
index 2a8674fee..497c28d60 100644
--- a/packages/translation/src/lang/it.json
+++ b/packages/translation/src/lang/it.json
@@ -3790,6 +3790,10 @@
"title": "Traduttori",
"subtitle": "{count} contribuendo alle traduzioni in molte lingue"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Librerie",
"subtitle": "{count} utilizzato nel Codice di Homarr"
diff --git a/packages/translation/src/lang/ja.json b/packages/translation/src/lang/ja.json
index 8aa74186c..72c636d84 100644
--- a/packages/translation/src/lang/ja.json
+++ b/packages/translation/src/lang/ja.json
@@ -3790,6 +3790,10 @@
"title": "翻訳者",
"subtitle": "{count} の多くの言語で翻訳を行っている"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "ライブラリ",
"subtitle": "{count} 個が Homarr のコードで使用されています"
diff --git a/packages/translation/src/lang/ko.json b/packages/translation/src/lang/ko.json
index ae1f07268..60d11950f 100644
--- a/packages/translation/src/lang/ko.json
+++ b/packages/translation/src/lang/ko.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/lt.json b/packages/translation/src/lang/lt.json
index 72e0fe42f..a28bed0de 100644
--- a/packages/translation/src/lang/lt.json
+++ b/packages/translation/src/lang/lt.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/lv.json b/packages/translation/src/lang/lv.json
index 8f91a8a70..6e06269c1 100644
--- a/packages/translation/src/lang/lv.json
+++ b/packages/translation/src/lang/lv.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/nl.json b/packages/translation/src/lang/nl.json
index e728d3779..1773a66ac 100644
--- a/packages/translation/src/lang/nl.json
+++ b/packages/translation/src/lang/nl.json
@@ -3790,6 +3790,10 @@
"title": "Vertalers",
"subtitle": "{count} bijdragen aan vertalingen in vele talen"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Bibliotheken",
"subtitle": "{count} gebruikt in de code van Homarr"
diff --git a/packages/translation/src/lang/no.json b/packages/translation/src/lang/no.json
index 09234ef69..5f7c0c6db 100644
--- a/packages/translation/src/lang/no.json
+++ b/packages/translation/src/lang/no.json
@@ -3790,6 +3790,10 @@
"title": "Oversettere",
"subtitle": "{count} bidrar med oversettelser på mange språk"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Biblioteker",
"subtitle": "{count} brukt i koden til Homarr"
diff --git a/packages/translation/src/lang/pl.json b/packages/translation/src/lang/pl.json
index 706f41c79..e28b2cb19 100644
--- a/packages/translation/src/lang/pl.json
+++ b/packages/translation/src/lang/pl.json
@@ -3790,6 +3790,10 @@
"title": "Tłumacze",
"subtitle": "{count} współtworzy tłumaczenia w wielu językach"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Biblioteki",
"subtitle": "{count} używanych w kodzie Homarr"
diff --git a/packages/translation/src/lang/pt.json b/packages/translation/src/lang/pt.json
index cb5d7e7cf..4862a07cb 100644
--- a/packages/translation/src/lang/pt.json
+++ b/packages/translation/src/lang/pt.json
@@ -164,7 +164,7 @@
},
"action": {
"login": {
- "label": "“Login”",
+ "label": "Login",
"labelWith": "Iniciar sessão com {provider}",
"notification": {
"success": {
@@ -3790,6 +3790,10 @@
"title": "Tradutores",
"subtitle": "{count} traduções contribuídas em vários idiomas"
},
+ "sponsors": {
+ "title": "Apoiadores",
+ "subtitle": "{count} apoiando o projeto financeiramente"
+ },
"libraries": {
"title": "Bibliotecas",
"subtitle": "{count} usadas no código do Homarr"
diff --git a/packages/translation/src/lang/ro.json b/packages/translation/src/lang/ro.json
index 034766afc..636db5678 100644
--- a/packages/translation/src/lang/ro.json
+++ b/packages/translation/src/lang/ro.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/ru.json b/packages/translation/src/lang/ru.json
index bed7a8ba7..c2f1203c5 100644
--- a/packages/translation/src/lang/ru.json
+++ b/packages/translation/src/lang/ru.json
@@ -3790,6 +3790,10 @@
"title": "Переводчики",
"subtitle": "{count} помогают с переводами на разные языки"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Библиотеки",
"subtitle": "{count} используются в коде Homarr"
diff --git a/packages/translation/src/lang/sk.json b/packages/translation/src/lang/sk.json
index 31dd9ec0b..e32d45860 100644
--- a/packages/translation/src/lang/sk.json
+++ b/packages/translation/src/lang/sk.json
@@ -3790,6 +3790,10 @@
"title": "Prekladatelia",
"subtitle": "{count} prispievanie prekladmi do mnohých jazykov"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Knižnice",
"subtitle": "{count} používané v Homarrskom kódexe"
diff --git a/packages/translation/src/lang/sl.json b/packages/translation/src/lang/sl.json
index 383e49a35..b156df881 100644
--- a/packages/translation/src/lang/sl.json
+++ b/packages/translation/src/lang/sl.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/sv.json b/packages/translation/src/lang/sv.json
index 056013ad7..0bd7991bb 100644
--- a/packages/translation/src/lang/sv.json
+++ b/packages/translation/src/lang/sv.json
@@ -3790,6 +3790,10 @@
"title": "Översättare",
"subtitle": "{count} bidrar med översättningar på flera olika språk"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/tr.json b/packages/translation/src/lang/tr.json
index 50e832ebc..2f14b98d3 100644
--- a/packages/translation/src/lang/tr.json
+++ b/packages/translation/src/lang/tr.json
@@ -3790,6 +3790,10 @@
"title": "Çevirmenler",
"subtitle": "Birçok kişi Homarr'ın {count} dile çevrilmesine katkı sağlıyor"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Kütüphaneler",
"subtitle": "Homarr kodlamasında {count} kütüphane kullanılıyor"
diff --git a/packages/translation/src/lang/uk.json b/packages/translation/src/lang/uk.json
index 1adefd44e..d0466bbda 100644
--- a/packages/translation/src/lang/uk.json
+++ b/packages/translation/src/lang/uk.json
@@ -3790,6 +3790,10 @@
"title": "Перекладачі",
"subtitle": "{count} - стільки людей допомагають перекладати багатьма мовами"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "Бібліотеки",
"subtitle": "{count} - стільки бібліотек використано в коді Homarr"
diff --git a/packages/translation/src/lang/vi.json b/packages/translation/src/lang/vi.json
index a69fb80fa..fc3c85549 100644
--- a/packages/translation/src/lang/vi.json
+++ b/packages/translation/src/lang/vi.json
@@ -3790,6 +3790,10 @@
"title": "",
"subtitle": ""
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "",
"subtitle": ""
diff --git a/packages/translation/src/lang/zh.json b/packages/translation/src/lang/zh.json
index 93281366e..3d6260c86 100644
--- a/packages/translation/src/lang/zh.json
+++ b/packages/translation/src/lang/zh.json
@@ -3790,6 +3790,10 @@
"title": "翻譯人員",
"subtitle": "{count} 翻譯了許多語言"
},
+ "sponsors": {
+ "title": "",
+ "subtitle": ""
+ },
"libraries": {
"title": "資料庫",
"subtitle": "{count} 於 Homarr 待碼中使用"
diff --git a/packages/validation/src/user.ts b/packages/validation/src/user.ts
index 6baaf8150..4d5bd1e77 100644
--- a/packages/validation/src/user.ts
+++ b/packages/validation/src/user.ts
@@ -138,14 +138,10 @@ export const userChangeColorSchemeSchema = z.object({
export const userFirstDayOfWeekSchema = z.object({
firstDayOfWeek: z
- .custom((value) => z.number().min(0).max(6).safeParse(value).success)
- .meta({
- override: {
- type: "integer",
- minimum: 0,
- maximum: 6,
- },
- }),
+ .number()
+ .min(0)
+ .max(6)
+ .transform((value) => value as DayOfWeek),
});
export const userPingIconsEnabledSchema = z.object({
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index addb9194c..62c00baee 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -130,14 +130,14 @@ catalogs:
specifier: ^3.41.1
version: 3.41.1
'@tanstack/react-query':
- specifier: ^5.99.1
- version: 5.99.2
+ specifier: ^5.100.5
+ version: 5.100.5
'@tanstack/react-query-devtools':
- specifier: ^5.99.1
- version: 5.99.2
+ specifier: ^5.100.5
+ version: 5.100.5
'@tanstack/react-query-next-experimental':
- specifier: ^5.99.1
- version: 5.99.2
+ specifier: ^5.100.5
+ version: 5.100.5
'@testcontainers/mysql':
specifier: ^11.14.0
version: 11.14.0
@@ -240,9 +240,6 @@ catalogs:
'@types/node':
specifier: ^24.12.2
version: 24.12.2
- '@types/node-cron':
- specifier: ^3.0.11
- version: 3.0.11
'@types/node-unifi':
specifier: ^2.5.1
version: 2.5.1
@@ -277,11 +274,11 @@ catalogs:
specifier: ^5.2.0
version: 5.2.0
'@vitest/coverage-v8':
- specifier: ^4.1.4
- version: 4.1.4
+ specifier: ^4.1.5
+ version: 4.1.5
'@vitest/ui':
- specifier: ^4.1.4
- version: 4.1.4
+ specifier: ^4.1.5
+ version: 4.1.5
'@xterm/addon-canvas':
specifier: ^0.7.0
version: 0.7.0
@@ -321,6 +318,9 @@ catalogs:
cookies:
specifier: ^0.9.1
version: 0.9.1
+ croner:
+ specifier: 10.0.1
+ version: 10.0.1
cross-env:
specifier: ^10.1.0
version: 10.1.0
@@ -337,8 +337,8 @@ catalogs:
specifier: ^0.2.9
version: 0.2.9
dockerode:
- specifier: ^4.0.10
- version: 4.0.10
+ specifier: ^5.0.0
+ version: 5.0.0
dotenv:
specifier: ^17.4.2
version: 17.4.2
@@ -379,11 +379,11 @@ catalogs:
specifier: ^6.1.1
version: 6.1.1
fast-xml-parser:
- specifier: ^5.7.1
- version: 5.7.1
+ specifier: ^5.7.2
+ version: 5.7.2
fastify:
- specifier: ^5.8.4
- version: 5.8.4
+ specifier: ^5.8.5
+ version: 5.8.5
flag-icons:
specifier: ^7.5.0
version: 7.5.0
@@ -397,8 +397,8 @@ catalogs:
specifier: 5.10.1
version: 5.10.1
isomorphic-dompurify:
- specifier: ^3.9.0
- version: 3.9.0
+ specifier: ^3.10.0
+ version: 3.10.0
jotai:
specifier: ^2.19.1
version: 2.19.1
@@ -424,8 +424,8 @@ catalogs:
specifier: ^0.4.1
version: 0.4.1
mysql2:
- specifier: 3.22.1
- version: 3.22.1
+ specifier: 3.22.2
+ version: 3.22.2
next:
specifier: 16.2.4
version: 16.2.4
@@ -435,9 +435,6 @@ catalogs:
next-intl:
specifier: 4.9.1
version: 4.9.1
- node-cron:
- specifier: ^4.2.1
- version: 4.2.1
node-loader:
specifier: ^2.1.0
version: 2.1.0
@@ -538,8 +535,8 @@ catalogs:
specifier: ^6.1.1
version: 6.1.1
vitest:
- specifier: ^4.1.4
- version: 4.1.4
+ specifier: ^4.1.5
+ version: 4.1.5
winston:
specifier: 3.19.0
version: 3.19.0
@@ -565,12 +562,12 @@ catalogs:
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.1'
+ axios@>=1.0.0 <1.8.2: '>=1.15.2'
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.14'
+ hono@<4.6.5: '>=4.12.15'
linkifyjs@<4.3.2: '>=4.3.2'
nanoid@>=4.0.0 <5.0.9: '>=5.1.9'
prismjs@<1.30.0: '>=1.30.0'
@@ -581,7 +578,7 @@ overrides:
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.8'
+ vite@>=5.0.0 <=5.4.18: '>=8.0.10'
patchedDependencies:
'@types/node-unifi':
@@ -627,10 +624,10 @@ importers:
version: 5.2.0(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@vitest/coverage-v8':
specifier: 'catalog:'
- version: 4.1.4(vitest@4.1.4)
+ version: 4.1.5(vitest@4.1.5)
'@vitest/ui':
specifier: 'catalog:'
- version: 4.1.4(vitest@4.1.4)
+ version: 4.1.5(vitest@4.1.5)
conventional-changelog-conventionalcommits:
specifier: 'catalog:'
version: 9.3.1
@@ -663,7 +660,7 @@ importers:
version: 6.1.1(typescript@5.9.3)(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vitest:
specifier: 'catalog:'
- version: 4.1.4(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.5)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
apps/nextjs:
dependencies:
@@ -754,6 +751,9 @@ importers:
'@homarr/spotlight':
specifier: workspace:^0.1.0
version: link:../../packages/spotlight
+ '@homarr/tasks':
+ specifier: workspace:^0.1.0
+ version: link:../tasks
'@homarr/translation':
specifier: workspace:^0.1.0
version: link:../../packages/translation
@@ -763,6 +763,9 @@ importers:
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../../packages/validation
+ '@homarr/websocket':
+ specifier: workspace:^0.1.0
+ version: link:../websocket
'@homarr/widgets':
specifier: workspace:^0.1.0
version: link:../../packages/widgets
@@ -792,22 +795,22 @@ importers:
version: 3.41.1(react@19.2.5)
'@tanstack/react-query':
specifier: 'catalog:'
- version: 5.99.2(react@19.2.5)
+ version: 5.100.5(react@19.2.5)
'@tanstack/react-query-devtools':
specifier: 'catalog:'
- version: 5.99.2(@tanstack/react-query@5.99.2(react@19.2.5))(react@19.2.5)
+ version: 5.100.5(@tanstack/react-query@5.100.5(react@19.2.5))(react@19.2.5)
'@tanstack/react-query-next-experimental':
specifier: 'catalog:'
- version: 5.99.2(@tanstack/react-query@5.99.2(react@19.2.5))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)
+ version: 5.100.5(@tanstack/react-query@5.100.5(react@19.2.5))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)
'@trpc/client':
specifier: 'catalog:'
version: 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)
'@trpc/next':
specifier: 'catalog:'
- version: 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3)
+ version: 11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3)
'@trpc/react-query':
specifier: 'catalog:'
- version: 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
+ version: 11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
'@trpc/server':
specifier: 'catalog:'
version: 11.16.0(typescript@5.9.3)
@@ -843,7 +846,7 @@ importers:
version: 13.0.6
isomorphic-dompurify:
specifier: 'catalog:'
- version: 3.9.0(@noble/hashes@2.0.1)
+ version: 3.10.0(@noble/hashes@2.0.1)
jotai:
specifier: 'catalog:'
version: 2.19.1(@babel/core@7.26.0)(@babel/template@7.28.6)(@types/react@19.2.14)(react@19.2.5)
@@ -947,9 +950,6 @@ importers:
'@homarr/core':
specifier: workspace:^
version: link:../../packages/core
- '@homarr/cron-job-api':
- specifier: workspace:^0.1.0
- version: link:../../packages/cron-job-api
'@homarr/cron-jobs':
specifier: workspace:^0.1.0
version: link:../../packages/cron-jobs
@@ -986,6 +986,9 @@ importers:
'@homarr/widgets':
specifier: workspace:^0.1.0
version: link:../../packages/widgets
+ croner:
+ specifier: 'catalog:'
+ version: 10.0.1
dayjs:
specifier: 'catalog:'
version: 1.11.20
@@ -994,7 +997,7 @@ importers:
version: 17.4.2
fastify:
specifier: 'catalog:'
- version: 5.8.4
+ version: 5.8.5
superjson:
specifier: 'catalog:'
version: 2.2.6
@@ -1148,9 +1151,6 @@ importers:
'@homarr/core':
specifier: workspace:^0.1.0
version: link:../core
- '@homarr/cron-job-api':
- specifier: workspace:^0.1.0
- version: link:../cron-job-api
'@homarr/cron-job-status':
specifier: workspace:^0.1.0
version: link:../cron-job-status
@@ -1201,19 +1201,19 @@ importers:
version: 1.4.0
'@tanstack/react-query':
specifier: 'catalog:'
- version: 5.99.2(react@19.2.5)
+ version: 5.100.5(react@19.2.5)
'@trpc/client':
specifier: 'catalog:'
version: 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)
'@trpc/react-query':
specifier: 'catalog:'
- version: 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
+ version: 11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
'@trpc/server':
specifier: 'catalog:'
version: 11.16.0(typescript@5.9.3)
'@trpc/tanstack-react-query':
specifier: 'catalog:'
- version: 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
+ version: 11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
lodash.clonedeep:
specifier: 'catalog:'
version: 4.5.0
@@ -1461,13 +1461,13 @@ importers:
version: 0.2.9
drizzle-orm:
specifier: 'catalog:'
- version: 0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.1(@types/node@24.12.2))(pg@8.20.0)
+ version: 0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.2(@types/node@24.12.2))(pg@8.20.0)
ioredis:
specifier: 'catalog:'
version: 5.10.1
mysql2:
specifier: 'catalog:'
- version: 3.22.1(@types/node@24.12.2)
+ version: 3.22.2(@types/node@24.12.2)
pg:
specifier: 'catalog:'
version: 8.20.0
@@ -1503,61 +1503,6 @@ importers:
specifier: 'catalog:'
version: 5.9.3
- packages/cron-job-api:
- dependencies:
- '@homarr/common':
- specifier: workspace:^0.1.0
- version: link:../common
- '@homarr/core':
- specifier: workspace:^0.1.0
- version: link:../core
- '@homarr/cron-jobs':
- specifier: workspace:^0.1.0
- version: link:../cron-jobs
- '@tanstack/react-query':
- specifier: 'catalog:'
- version: 5.99.2(react@19.2.5)
- '@trpc/client':
- specifier: 'catalog:'
- version: 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)
- '@trpc/server':
- specifier: 'catalog:'
- version: 11.16.0(typescript@5.9.3)
- '@trpc/tanstack-react-query':
- specifier: 'catalog:'
- version: 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
- node-cron:
- specifier: 'catalog:'
- version: 4.2.1
- react:
- specifier: 'catalog:'
- version: 19.2.5
- zod:
- specifier: 'catalog:'
- version: 4.3.6
- devDependencies:
- '@homarr/eslint-config':
- specifier: workspace:^0.2.0
- version: link:../../tooling/eslint
- '@homarr/prettier-config':
- specifier: workspace:^0.1.0
- version: link:../../tooling/prettier
- '@homarr/tsconfig':
- specifier: workspace:^0.1.0
- version: link:../../tooling/typescript
- '@types/node-cron':
- specifier: 'catalog:'
- version: 3.0.11
- '@types/react':
- specifier: 'catalog:'
- version: 19.2.14
- eslint:
- specifier: 'catalog:'
- version: 9.39.4
- typescript:
- specifier: 'catalog:'
- version: 5.9.3
-
packages/cron-job-status:
dependencies:
'@homarr/redis':
@@ -1633,6 +1578,9 @@ importers:
semver-parser:
specifier: 'catalog:'
version: 4.1.8
+ zod:
+ specifier: 'catalog:'
+ version: 4.3.6
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -1661,9 +1609,9 @@ importers:
'@homarr/db':
specifier: workspace:^0.1.0
version: link:../db
- node-cron:
+ croner:
specifier: 'catalog:'
- version: 4.2.1
+ version: 10.0.1
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -1674,9 +1622,6 @@ importers:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
- '@types/node-cron':
- specifier: 'catalog:'
- version: 3.0.11
eslint:
specifier: 'catalog:'
version: 9.39.4
@@ -1724,13 +1669,13 @@ importers:
version: 0.31.10
drizzle-orm:
specifier: 'catalog:'
- version: 0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.1(@types/node@24.12.2))(pg@8.20.0)
+ version: 0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.2(@types/node@24.12.2))(pg@8.20.0)
drizzle-zod:
specifier: 'catalog:'
- version: 0.8.3(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.1(@types/node@24.12.2))(pg@8.20.0))(zod@4.3.6)
+ version: 0.8.3(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.2(@types/node@24.12.2))(pg@8.20.0))(zod@4.3.6)
mysql2:
specifier: 'catalog:'
- version: 3.22.1(@types/node@24.12.2)
+ version: 3.22.2(@types/node@24.12.2)
pg:
specifier: 'catalog:'
version: 8.20.0
@@ -1779,7 +1724,7 @@ importers:
version: link:../common
fast-xml-parser:
specifier: 'catalog:'
- version: 5.7.1
+ version: 5.7.2
zod:
specifier: 'catalog:'
version: 4.3.6
@@ -1813,7 +1758,7 @@ importers:
version: link:../core
dockerode:
specifier: 'catalog:'
- version: 4.0.10
+ version: 5.0.0
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -3895,7 +3840,7 @@ packages:
resolution: {integrity: sha512-kz323qIQkNQElEGroo/E9MKPDuIR5pkuk/XEWd50K+cSEKdmdiYx0PKWUdaNY2ecJYngtF+njDMsMKplL6zfEg==}
engines: {node: '>=18.14.1'}
peerDependencies:
- hono: '>=4.12.14'
+ hono: '>=4.12.15'
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
@@ -4383,7 +4328,6 @@ packages:
'@million/lint@1.0.14':
resolution: {integrity: sha512-u6/kglVwZRu5+GMmtkNlGLqJVkgTl0TtM+hLa9rBg7pldx+5NG5bk45NvL37uZmAr2Xfa1C6qHb7GrFwfP372g==}
- deprecated: This package is no longer maintained
hasBin: true
'@ndaidong/bellajs@12.0.1':
@@ -5198,27 +5142,27 @@ packages:
resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
engines: {node: '>=12'}
- '@tanstack/query-core@5.99.2':
- resolution: {integrity: sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==}
+ '@tanstack/query-core@5.100.5':
+ resolution: {integrity: sha512-t20KrhKkf0HXzqQkPbJ5erhFesup68BAbwFgYmTrS7bxMF7O5MdmL8jUkik4thsG7Hg00fblz30h6yF1d5TxGg==}
- '@tanstack/query-devtools@5.99.2':
- resolution: {integrity: sha512-TEF1d+RYO9l8oeCwgzmOHIgKwAzXQmw2s/ny2bW8qeg2OMkkLjALfVEivgCMR3OL/jVdMmeTPX56WrV+uvYJFg==}
+ '@tanstack/query-devtools@5.100.5':
+ resolution: {integrity: sha512-SuCkVCqqliRYJvm+LEL2U/TcFv92zTnHj6OGrJFHp1v/RsiwamI+ZDgQzbeUrLsJb8/Nj/52aIw0NyDMcVHl4A==}
- '@tanstack/react-query-devtools@5.99.2':
- resolution: {integrity: sha512-8txkK9A9XBNTB8RoxVgfp6W3qwBr25tNP10L4yu3KuyhAdEvccECfIRzesSwMVk/wpVVioAr+hbMtUkMMF+WVw==}
+ '@tanstack/react-query-devtools@5.100.5':
+ resolution: {integrity: sha512-bItQERx7dJoiI0WEoS4tIrvNnmk4kUYsaQLdIpm4o9Kttmsi5B6xlY6JBDkavstR3hH/R2+VT5dr3L5LBFPW4g==}
peerDependencies:
- '@tanstack/react-query': ^5.99.2
+ '@tanstack/react-query': ^5.100.5
react: ^18 || ^19
- '@tanstack/react-query-next-experimental@5.99.2':
- resolution: {integrity: sha512-UarMaRNtIO3PF/KhZaJyxoKuqWLx9VjalgVd30svOlxjVyz9cRgZrr8H5xnFsfSPJkk/KZ/nedmN5cRf9jBwvw==}
+ '@tanstack/react-query-next-experimental@5.100.5':
+ resolution: {integrity: sha512-v6XsDcwHQFIMVqwUx9qvuhTnTEN10mvKm9Yfojfo5C1aOUx9dKZGDeL5NM+63GHlR589/Oeba7cCyEQi97nwYQ==}
peerDependencies:
- '@tanstack/react-query': ^5.99.2
+ '@tanstack/react-query': ^5.100.5
next: ^13 || ^14 || ^15 || ^16
react: ^18 || ^19
- '@tanstack/react-query@5.99.2':
- resolution: {integrity: sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA==}
+ '@tanstack/react-query@5.100.5':
+ resolution: {integrity: sha512-aNwj1mi2v2bQ9IxkyR1grLOUkv3BYWoykHy9KDyLNbjC3tsahbOHJibK+Wjtr1wRhG59/AvJhiJG5OlthaCgJA==}
peerDependencies:
react: ^18 || ^19
@@ -5688,9 +5632,6 @@ packages:
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
- '@types/node-cron@3.0.11':
- resolution: {integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==}
-
'@types/node-fetch@2.6.13':
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
@@ -5863,22 +5804,22 @@ packages:
resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==}
engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
- vite: '>=8.0.8'
+ vite: '>=8.0.10'
- '@vitest/coverage-v8@4.1.4':
- resolution: {integrity: sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w==}
+ '@vitest/coverage-v8@4.1.5':
+ resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==}
peerDependencies:
- '@vitest/browser': 4.1.4
- vitest: 4.1.4
+ '@vitest/browser': 4.1.5
+ vitest: 4.1.5
peerDependenciesMeta:
'@vitest/browser':
optional: true
- '@vitest/expect@4.1.4':
- resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==}
+ '@vitest/expect@4.1.5':
+ resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==}
- '@vitest/mocker@4.1.4':
- resolution: {integrity: sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==}
+ '@vitest/mocker@4.1.5':
+ resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==}
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -5888,25 +5829,25 @@ packages:
vite:
optional: true
- '@vitest/pretty-format@4.1.4':
- resolution: {integrity: sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==}
+ '@vitest/pretty-format@4.1.5':
+ resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==}
- '@vitest/runner@4.1.4':
- resolution: {integrity: sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==}
+ '@vitest/runner@4.1.5':
+ resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==}
- '@vitest/snapshot@4.1.4':
- resolution: {integrity: sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==}
+ '@vitest/snapshot@4.1.5':
+ resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==}
- '@vitest/spy@4.1.4':
- resolution: {integrity: sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==}
+ '@vitest/spy@4.1.5':
+ resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==}
- '@vitest/ui@4.1.4':
- resolution: {integrity: sha512-EgFR7nlj5iTDYZYCvavjFokNYwr3c3ry0sFiCg+N7B233Nwp+NNx7eoF/XvMWDCKY71xXAG3kFkt97ZHBJVL8A==}
+ '@vitest/ui@4.1.5':
+ resolution: {integrity: sha512-3Z9HNFiV0IF1fk0JPiK+7kE1GcaIPefQQIBYur6PM5yFIq6agys3uqP/0t966e1wXfmjbRCHDe7qW236Xjwnag==}
peerDependencies:
- vitest: 4.1.4
+ vitest: 4.1.5
- '@vitest/utils@4.1.4':
- resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==}
+ '@vitest/utils@4.1.5':
+ resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==}
'@webassemblyjs/ast@1.14.1':
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
@@ -6635,6 +6576,10 @@ packages:
resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
engines: {node: '>= 14'}
+ croner@10.0.1:
+ resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==}
+ engines: {node: '>=18.0'}
+
cross-env@10.1.0:
resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==}
engines: {node: '>=20'}
@@ -6872,6 +6817,10 @@ packages:
resolution: {integrity: sha512-8L/P9JynLBiG7/coiA4FlQXegHltRqS0a+KqI44P1zgQh8QLHTg7FKOwhkBgSJwZTeHsq30WRoVFLuwkfK0YFg==}
engines: {node: '>= 8.0'}
+ dockerode@5.0.0:
+ resolution: {integrity: sha512-C52mvJ+7lcyhWNfrzVfFsbTrBfy/ezE9FGEYLpu17FUeBcCkxERk9nN7uDl/478ynDiQ4U+5DbQC2vENHkVEtQ==}
+ engines: {node: '>= 14.17'}
+
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
@@ -6882,11 +6831,8 @@ packages:
dom-walk@0.1.2:
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
- dompurify@3.3.3:
- resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==}
-
- dompurify@3.4.0:
- resolution: {integrity: sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==}
+ dompurify@3.4.1:
+ resolution: {integrity: sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==}
dot-prop@5.3.0:
resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
@@ -7399,8 +7345,8 @@ packages:
fast-xml-builder@1.1.5:
resolution: {integrity: sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==}
- fast-xml-parser@5.7.1:
- resolution: {integrity: sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==}
+ fast-xml-parser@5.7.2:
+ resolution: {integrity: sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==}
hasBin: true
faster-babel-types@0.1.0:
@@ -7409,8 +7355,8 @@ packages:
peerDependencies:
'@babel/types': ^7
- fastify@5.8.4:
- resolution: {integrity: sha512-sa42J1xylbBAYUWALSBoyXKPDUvM3OoNOibIefA+Oha57FryXKKCZarA1iDntOCWp3O35voZLuDg2mdODXtPzQ==}
+ fastify@5.8.5:
+ resolution: {integrity: sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==}
fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
@@ -7495,15 +7441,6 @@ packages:
fn.name@1.1.0:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
- follow-redirects@1.15.9:
- resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
- engines: {node: '>=4.0'}
- peerDependencies:
- debug: '*'
- peerDependenciesMeta:
- debug:
- optional: true
-
follow-redirects@1.16.0:
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
engines: {node: '>=4.0'}
@@ -7745,8 +7682,8 @@ packages:
highlightjs-vue@1.0.0:
resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==}
- hono@4.12.14:
- resolution: {integrity: sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==}
+ hono@4.12.15:
+ resolution: {integrity: sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==}
engines: {node: '>=16.9.0'}
hook-std@4.0.0:
@@ -8119,8 +8056,8 @@ packages:
resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==}
engines: {node: '>=18'}
- isomorphic-dompurify@3.9.0:
- resolution: {integrity: sha512-3REwJAnIqjWO7qbfyKWMBI7hUHFhqUor7weFG3WbJW6Enq3V7cx60QuurWjGUXxbX57Iy4RJIkAZFObAqiqpxw==}
+ isomorphic-dompurify@3.10.0:
+ resolution: {integrity: sha512-Gj2duy4dACsP/FLPvwJ3+MXTlGtOo+O4yfpA0jdxuz/sZlbZzazGzScajOHRwH7PCy4j3bh5ibLGJY4/Rb5kGQ==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
isomorphic-fetch@3.0.0:
@@ -8664,8 +8601,8 @@ packages:
engines: {node: '>=8', npm: '>=5'}
hasBin: true
- mysql2@3.22.1:
- resolution: {integrity: sha512-48+9UXehKyxxiP2pqCxUq+MSFvX+v41jwsSpFDQO/jAoFuAELutBGJUhWJnDbe82/OBlIhSBMC82WeonmznT/Q==}
+ mysql2@3.22.2:
+ resolution: {integrity: sha512-snC/L6YoCJPFpozZo3p3hiOlt9ItQ7sCnLSziFLlIttEzsPhrdcPT8g21BiQ7Oqif25W4Xq1IFuBzBvoFYDf0Q==}
engines: {node: '>= 8.0'}
peerDependencies:
'@types/node': '>= 8'
@@ -8785,10 +8722,6 @@ packages:
resolution: {integrity: sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==}
engines: {node: ^18 || ^20 || >= 21}
- node-cron@4.2.1:
- resolution: {integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==}
- engines: {node: '>=6.0.0'}
-
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@@ -10783,10 +10716,12 @@ packages:
uuid@10.0.0:
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
+ deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
hasBin: true
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
hasBin: true
validate-npm-package-license@3.0.4:
@@ -10823,7 +10758,7 @@ packages:
vite-tsconfig-paths@6.1.1:
resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==}
peerDependencies:
- vite: '>=8.0.8'
+ vite: '>=8.0.10'
vite@7.1.12:
resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==}
@@ -10865,20 +10800,20 @@ packages:
yaml:
optional: true
- vitest@4.1.4:
- resolution: {integrity: sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==}
+ vitest@4.1.5:
+ resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@opentelemetry/api': ^1.9.0
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
- '@vitest/browser-playwright': 4.1.4
- '@vitest/browser-preview': 4.1.4
- '@vitest/browser-webdriverio': 4.1.4
- '@vitest/coverage-istanbul': 4.1.4
- '@vitest/coverage-v8': 4.1.4
- '@vitest/ui': 4.1.4
+ '@vitest/browser-playwright': 4.1.5
+ '@vitest/browser-preview': 4.1.5
+ '@vitest/browser-webdriverio': 4.1.5
+ '@vitest/coverage-istanbul': 4.1.5
+ '@vitest/coverage-v8': 4.1.5
+ '@vitest/ui': 4.1.5
happy-dom: '*'
jsdom: '*'
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -11857,7 +11792,7 @@ snapshots:
dependencies:
'@ndaidong/bellajs': 12.0.1
cross-fetch: 4.1.0
- fast-xml-parser: 5.7.1
+ fast-xml-parser: 5.7.2
html-entities: 2.6.0
transitivePeerDependencies:
- encoding
@@ -11989,9 +11924,9 @@ snapshots:
- undici
- utf-8-validate
- '@hono/node-server@1.13.0(hono@4.12.14)':
+ '@hono/node-server@1.13.0(hono@4.12.15)':
dependencies:
- hono: 4.12.14
+ hono: 4.12.15
'@humanfs/core@0.19.1': {}
@@ -12446,7 +12381,7 @@ snapshots:
'@axiomhq/js': 1.0.0-rc.3
'@babel/core': 7.26.0
'@babel/types': 7.26.0
- '@hono/node-server': 1.13.0(hono@4.12.14)
+ '@hono/node-server': 1.13.0(hono@4.12.15)
'@million/install': 1.0.14
'@rollup/pluginutils': 5.1.0
'@rrweb/types': 2.0.0-alpha.16
@@ -12454,7 +12389,7 @@ snapshots:
ci-info: 4.0.0
esbuild: 0.28.0
faster-babel-types: 0.1.0(@babel/types@7.26.0)
- hono: 4.12.14
+ hono: 4.12.15
isomorphic-fetch: 3.0.0
nanoid: 5.1.9
ohash: 1.1.4
@@ -13509,25 +13444,25 @@ snapshots:
dependencies:
remove-accents: 0.5.0
- '@tanstack/query-core@5.99.2': {}
+ '@tanstack/query-core@5.100.5': {}
- '@tanstack/query-devtools@5.99.2': {}
+ '@tanstack/query-devtools@5.100.5': {}
- '@tanstack/react-query-devtools@5.99.2(@tanstack/react-query@5.99.2(react@19.2.5))(react@19.2.5)':
+ '@tanstack/react-query-devtools@5.100.5(@tanstack/react-query@5.100.5(react@19.2.5))(react@19.2.5)':
dependencies:
- '@tanstack/query-devtools': 5.99.2
- '@tanstack/react-query': 5.99.2(react@19.2.5)
+ '@tanstack/query-devtools': 5.100.5
+ '@tanstack/react-query': 5.100.5(react@19.2.5)
react: 19.2.5
- '@tanstack/react-query-next-experimental@5.99.2(@tanstack/react-query@5.99.2(react@19.2.5))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)':
+ '@tanstack/react-query-next-experimental@5.100.5(@tanstack/react-query@5.100.5(react@19.2.5))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)':
dependencies:
- '@tanstack/react-query': 5.99.2(react@19.2.5)
+ '@tanstack/react-query': 5.100.5(react@19.2.5)
next: 16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)
react: 19.2.5
- '@tanstack/react-query@5.99.2(react@19.2.5)':
+ '@tanstack/react-query@5.100.5(react@19.2.5)':
dependencies:
- '@tanstack/query-core': 5.99.2
+ '@tanstack/query-core': 5.100.5
react: 19.2.5
'@tanstack/react-table@8.20.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
@@ -13800,7 +13735,7 @@ snapshots:
'@trpc/server': 11.16.0(typescript@5.9.3)
typescript: 5.9.3
- '@trpc/next@11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3)':
+ '@trpc/next@11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(next@16.2.4(@babel/core@7.26.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3)':
dependencies:
'@trpc/client': 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)
'@trpc/server': 11.16.0(typescript@5.9.3)
@@ -13809,12 +13744,12 @@ snapshots:
react-dom: 19.2.5(react@19.2.5)
typescript: 5.9.3
optionalDependencies:
- '@tanstack/react-query': 5.99.2(react@19.2.5)
- '@trpc/react-query': 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
+ '@tanstack/react-query': 5.100.5(react@19.2.5)
+ '@trpc/react-query': 11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)
- '@trpc/react-query@11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)':
+ '@trpc/react-query@11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)':
dependencies:
- '@tanstack/react-query': 5.99.2(react@19.2.5)
+ '@tanstack/react-query': 5.100.5(react@19.2.5)
'@trpc/client': 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)
'@trpc/server': 11.16.0(typescript@5.9.3)
react: 19.2.5
@@ -13824,9 +13759,9 @@ snapshots:
dependencies:
typescript: 5.9.3
- '@trpc/tanstack-react-query@11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)':
+ '@trpc/tanstack-react-query@11.16.0(@tanstack/react-query@5.100.5(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)':
dependencies:
- '@tanstack/react-query': 5.99.2(react@19.2.5)
+ '@tanstack/react-query': 5.100.5(react@19.2.5)
'@trpc/client': 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)
'@trpc/server': 11.16.0(typescript@5.9.3)
react: 19.2.5
@@ -14011,8 +13946,6 @@ snapshots:
'@types/ms@2.1.0': {}
- '@types/node-cron@3.0.11': {}
-
'@types/node-fetch@2.6.13':
dependencies:
'@types/node': 24.12.2
@@ -14246,10 +14179,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@vitest/coverage-v8@4.1.4(vitest@4.1.4)':
+ '@vitest/coverage-v8@4.1.5(vitest@4.1.5)':
dependencies:
'@bcoe/v8-coverage': 1.0.2
- '@vitest/utils': 4.1.4
+ '@vitest/utils': 4.1.5
ast-v8-to-istanbul: 1.0.0
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
@@ -14258,57 +14191,57 @@ snapshots:
obug: 2.1.1
std-env: 4.0.0
tinyrainbow: 3.1.0
- vitest: 4.1.4(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ vitest: 4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.5)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
- '@vitest/expect@4.1.4':
+ '@vitest/expect@4.1.5':
dependencies:
'@standard-schema/spec': 1.1.0
'@types/chai': 5.2.2
- '@vitest/spy': 4.1.4
- '@vitest/utils': 4.1.4
+ '@vitest/spy': 4.1.5
+ '@vitest/utils': 4.1.5
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.4(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
+ '@vitest/mocker@4.1.5(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
- '@vitest/spy': 4.1.4
+ '@vitest/spy': 4.1.5
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
- '@vitest/pretty-format@4.1.4':
+ '@vitest/pretty-format@4.1.5':
dependencies:
tinyrainbow: 3.1.0
- '@vitest/runner@4.1.4':
+ '@vitest/runner@4.1.5':
dependencies:
- '@vitest/utils': 4.1.4
+ '@vitest/utils': 4.1.5
pathe: 2.0.3
- '@vitest/snapshot@4.1.4':
+ '@vitest/snapshot@4.1.5':
dependencies:
- '@vitest/pretty-format': 4.1.4
- '@vitest/utils': 4.1.4
+ '@vitest/pretty-format': 4.1.5
+ '@vitest/utils': 4.1.5
magic-string: 0.30.21
pathe: 2.0.3
- '@vitest/spy@4.1.4': {}
+ '@vitest/spy@4.1.5': {}
- '@vitest/ui@4.1.4(vitest@4.1.4)':
+ '@vitest/ui@4.1.5(vitest@4.1.5)':
dependencies:
- '@vitest/utils': 4.1.4
+ '@vitest/utils': 4.1.5
fflate: 0.8.2
flatted: 3.4.2
pathe: 2.0.3
sirv: 3.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.1.0
- vitest: 4.1.4(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ vitest: 4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.5)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
- '@vitest/utils@4.1.4':
+ '@vitest/utils@4.1.5':
dependencies:
- '@vitest/pretty-format': 4.1.4
+ '@vitest/pretty-format': 4.1.5
convert-source-map: 2.0.0
tinyrainbow: 3.1.0
@@ -14665,7 +14598,7 @@ snapshots:
axios@1.9.0:
dependencies:
- follow-redirects: 1.15.9
+ follow-redirects: 1.16.0
form-data: 4.0.5
proxy-from-env: 1.1.0
transitivePeerDependencies:
@@ -15093,6 +15026,8 @@ snapshots:
crc-32: 1.2.2
readable-stream: 4.5.2
+ croner@10.0.1: {}
+
cross-env@10.1.0:
dependencies:
'@epic-web/invariant': 1.0.0
@@ -15308,6 +15243,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ dockerode@5.0.0:
+ dependencies:
+ '@balena/dockerignore': 1.0.2
+ '@grpc/grpc-js': 1.12.5
+ '@grpc/proto-loader': 0.7.13
+ docker-modem: 5.0.7
+ protobufjs: 7.4.0
+ tar-fs: 2.1.4
+ transitivePeerDependencies:
+ - supports-color
+
doctrine@2.1.0:
dependencies:
esutils: 2.0.3
@@ -15319,11 +15265,7 @@ snapshots:
dom-walk@0.1.2: {}
- dompurify@3.3.3:
- optionalDependencies:
- '@types/trusted-types': 2.0.7
-
- dompurify@3.4.0:
+ dompurify@3.4.1:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -15357,19 +15299,19 @@ snapshots:
esbuild: 0.25.12
tsx: 4.21.0
- drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.1(@types/node@24.12.2))(pg@8.20.0):
+ drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.2(@types/node@24.12.2))(pg@8.20.0):
optionalDependencies:
'@libsql/client-wasm': 0.14.0
'@types/better-sqlite3': 7.6.13
'@types/pg': 8.20.0
better-sqlite3: 12.9.0
gel: 2.0.0
- mysql2: 3.22.1(@types/node@24.12.2)
+ mysql2: 3.22.2(@types/node@24.12.2)
pg: 8.20.0
- drizzle-zod@0.8.3(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.1(@types/node@24.12.2))(pg@8.20.0))(zod@4.3.6):
+ drizzle-zod@0.8.3(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.2(@types/node@24.12.2))(pg@8.20.0))(zod@4.3.6):
dependencies:
- drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.1(@types/node@24.12.2))(pg@8.20.0)
+ drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.9.0)(gel@2.0.0)(mysql2@3.22.2(@types/node@24.12.2))(pg@8.20.0)
zod: 4.3.6
dunder-proto@1.0.1:
@@ -15970,7 +15912,7 @@ snapshots:
dependencies:
path-expression-matcher: 1.5.0
- fast-xml-parser@5.7.1:
+ fast-xml-parser@5.7.2:
dependencies:
'@nodable/entities': 2.1.0
fast-xml-builder: 1.1.5
@@ -15981,7 +15923,7 @@ snapshots:
dependencies:
'@babel/types': 7.26.0
- fastify@5.8.4:
+ fastify@5.8.5:
dependencies:
'@fastify/ajv-compiler': 4.0.5
'@fastify/error': 4.2.0
@@ -16074,8 +16016,6 @@ snapshots:
fn.name@1.1.0: {}
- follow-redirects@1.15.9: {}
-
follow-redirects@1.16.0: {}
for-each@0.3.5:
@@ -16351,7 +16291,7 @@ snapshots:
highlightjs-vue@1.0.0: {}
- hono@4.12.14: {}
+ hono@4.12.15: {}
hook-std@4.0.0: {}
@@ -16680,9 +16620,9 @@ snapshots:
isexe@3.1.5:
optional: true
- isomorphic-dompurify@3.9.0(@noble/hashes@2.0.1):
+ isomorphic-dompurify@3.10.0(@noble/hashes@2.0.1):
dependencies:
- dompurify: 3.4.0
+ dompurify: 3.4.1
jsdom: 29.0.2(@noble/hashes@2.0.1)
transitivePeerDependencies:
- '@noble/hashes'
@@ -17345,7 +17285,7 @@ snapshots:
'@babel/runtime': 7.29.2
global: 4.4.0
- mysql2@3.22.1(@types/node@24.12.2):
+ mysql2@3.22.2(@types/node@24.12.2):
dependencies:
'@types/node': 24.12.2
aws-ssl-profiles: 1.1.2
@@ -17484,8 +17424,6 @@ snapshots:
node-addon-api@8.3.1: {}
- node-cron@4.2.1: {}
-
node-domexception@1.0.0: {}
node-emoji@2.2.0:
@@ -19192,7 +19130,7 @@ snapshots:
classnames: 2.5.1
css.escape: 1.5.1
deep-extend: 0.6.0
- dompurify: 3.3.3
+ dompurify: 3.4.1
ieee754: 1.2.1
immutable: 3.8.2
js-file-download: 0.4.12
@@ -19848,15 +19786,15 @@ snapshots:
tsx: 4.21.0
yaml: 2.8.3
- vitest@4.1.4(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
+ vitest@4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.5)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
- '@vitest/expect': 4.1.4
- '@vitest/mocker': 4.1.4(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
- '@vitest/pretty-format': 4.1.4
- '@vitest/runner': 4.1.4
- '@vitest/snapshot': 4.1.4
- '@vitest/spy': 4.1.4
- '@vitest/utils': 4.1.4
+ '@vitest/expect': 4.1.5
+ '@vitest/mocker': 4.1.5(vite@7.1.12(@types/node@24.12.2)(sass@1.99.0)(sugarss@5.0.0(postcss@8.5.8))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ '@vitest/pretty-format': 4.1.5
+ '@vitest/runner': 4.1.5
+ '@vitest/snapshot': 4.1.5
+ '@vitest/spy': 4.1.5
+ '@vitest/utils': 4.1.5
es-module-lexer: 2.0.0
expect-type: 1.3.0
magic-string: 0.30.21
@@ -19872,8 +19810,8 @@ snapshots:
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.12.2
- '@vitest/coverage-v8': 4.1.4(vitest@4.1.4)
- '@vitest/ui': 4.1.4(vitest@4.1.4)
+ '@vitest/coverage-v8': 4.1.5(vitest@4.1.5)
+ '@vitest/ui': 4.1.5(vitest@4.1.5)
jsdom: 29.0.2(@noble/hashes@2.0.1)
transitivePeerDependencies:
- msw
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index b8c548d03..e70b39a68 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -49,9 +49,9 @@ catalog:
"@semantic-release/release-notes-generator": ^14.1.0
"@t3-oss/env-nextjs": ^0.13.11
"@tabler/icons-react": ^3.41.1
- "@tanstack/react-query": ^5.99.1
- "@tanstack/react-query-devtools": ^5.99.1
- "@tanstack/react-query-next-experimental": ^5.99.1
+ "@tanstack/react-query": ^5.100.5
+ "@tanstack/react-query-devtools": ^5.100.5
+ "@tanstack/react-query-next-experimental": ^5.100.5
"@testcontainers/mysql": ^11.14.0
"@testcontainers/postgresql": ^11.14.0
"@testcontainers/redis": ^11.14.0
@@ -86,7 +86,6 @@ catalog:
"@types/css-modules": ^1.0.5
"@types/dockerode": ^4.0.1
"@types/node": ^24.12.2
- "@types/node-cron": ^3.0.11
"@types/node-unifi": ^2.5.1
"@types/pg": ^8.20.0
"@types/prismjs": ^1.26.6
@@ -98,8 +97,8 @@ catalog:
"@types/xml2js": ^0.4.14
"@umami/node": ^0.4.0
"@vitejs/plugin-react": ^5.2.0
- "@vitest/coverage-v8": ^4.1.4
- "@vitest/ui": ^4.1.4
+ "@vitest/coverage-v8": ^4.1.5
+ "@vitest/ui": ^4.1.5
"@xterm/addon-canvas": ^0.7.0
"@xterm/addon-fit": 0.11.0
"@xterm/xterm": ^6.0.0
@@ -113,12 +112,13 @@ catalog:
conventional-changelog-conventionalcommits: ^9.3.1
cookie: 1.1.1
cookies: ^0.9.1
+ croner: 10.0.1
cross-env: ^10.1.0
crypto-js: ^4.2.0
dayjs: ^1.11.20
deepmerge: 4.3.1
dns-caching: ^0.2.9
- dockerode: ^4.0.10
+ dockerode: ^5.0.0
dotenv: ^17.4.2
dotenv-cli: ^11.0.0
drizzle-kit: ^0.31.10
@@ -132,13 +132,13 @@ catalog:
eslint-plugin-jsx-a11y: ^6.10.2
eslint-plugin-react: ^7.37.5
eslint-plugin-react-hooks: ^6.1.1
- fast-xml-parser: ^5.7.1
- fastify: ^5.8.4
+ fast-xml-parser: ^5.7.2
+ fastify: ^5.8.5
flag-icons: ^7.5.0
glob: ^13.0.6
ical.js: ^2.2.1
ioredis: 5.10.1
- isomorphic-dompurify: ^3.9.0
+ isomorphic-dompurify: ^3.10.0
jotai: ^2.19.1
jsdom: ^29.0.2
json5: ^2.2.3
@@ -147,11 +147,10 @@ catalog:
mantine-form-zod-resolver: ^1.3.0
mantine-react-table: 2.0.0-beta.9
maria2: ^0.4.1
- mysql2: 3.22.1
+ mysql2: 3.22.2
next: 16.2.4
next-auth: 5.0.0-beta.31
next-intl: 4.9.1
- node-cron: ^4.2.1
node-loader: ^2.1.0
octokit: ^5.0.5
pg: ^8.20.0
@@ -185,7 +184,7 @@ catalog:
use-deep-compare-effect: ^1.8.1
video.js: ^8.23.7
vite-tsconfig-paths: ^6.1.1
- vitest: ^4.1.4
+ vitest: ^4.1.5
winston: 3.19.0
ws: ^8.20.0
xml2js: ^0.6.2
diff --git a/scripts/run.sh b/scripts/run.sh
index 4d90499d5..fc6e80ba4 100644
--- a/scripts/run.sh
+++ b/scripts/run.sh
@@ -16,8 +16,6 @@ fi
# Auth secret is generated every time the container starts as it is required, but not used because we don't need JWTs or Mail hashing
export AUTH_SECRET=$(openssl rand -base64 32)
-# 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)
# Support secret file for encryption key (is not empty and file exists)
if [ -n "$SECRET_ENCRYPTION_KEY_FILE" ] && [ -f "$SECRET_ENCRYPTION_KEY_FILE" ]; then
@@ -43,21 +41,13 @@ else
REDIS_PID=$!
fi
-
-
-node apps/tasks/tasks.cjs &
-TASKS_PID=$!
-
-node apps/websocket/wssServer.cjs &
-WSS_PID=$!
-
node apps/nextjs/server.js &
NEXTJS_PID=$!
# Function to handle SIGTERM and shut down services
terminate() {
echo "Received SIGTERM. Shutting down..."
- kill -TERM $NGINX_PID $TASKS_PID $WSS_PID $NEXTJS_PID 2>/dev/null
+ kill -TERM $NGINX_PID $NEXTJS_PID 2>/dev/null
wait
# kill redis-server last because of logging of other services and only if $REDIS_PID is set
if [ -n "$REDIS_PID" ]; then
diff --git a/scripts/update-contributors.mjs b/scripts/update-contributors.mjs
index 6bf17a250..2c7fb1054 100644
--- a/scripts/update-contributors.mjs
+++ b/scripts/update-contributors.mjs
@@ -1,14 +1,12 @@
import fs from "fs/promises";
const sources = {
- crowdin: [
- { projectId: 534422 },
- { projectId: 742587 },
- ],
+ crowdin: [{ projectId: 534422 }, { projectId: 742587 }],
github: [
{ slug: "ajnart", repository: "homarr" },
{ slug: "homarr-labs", repository: "homarr" },
],
+ opencollective: [{ slug: "homarr" }],
};
const env = {
@@ -47,11 +45,55 @@ const fetchCrowdinMembers = async (projectId) => {
};
const response = await fetch(url, options);
- const data = await response.json();
+ const result = await response.json();
- return data.data.flatMap((data) => data.data).map(contributor => ({
- username: contributor.username,
- avatarUrl: contributor.avatarUrl,
+ return result.data
+ .flatMap(({ data }) => data)
+ .map((contributor) => ({
+ username: contributor.username,
+ avatarUrl: contributor.avatarUrl,
+ }));
+};
+
+const fetchOpenCollectiveContributors = async (slug) => {
+ const query = `
+ query($slug: String!) {
+ account(slug: $slug) {
+ ... on Organization {
+ members(role: [BACKER]) {
+ nodes {
+ role
+ account {
+ id
+ name
+ slug
+ imageUrl
+ }
+ }
+ }
+ }
+ }
+ }
+ `;
+
+ const response = await fetch("https://api.opencollective.com/graphql/v2", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query,
+ variables: { slug },
+ }),
+ });
+
+ const result = await response.json();
+ const members = result.data?.account?.members?.nodes || [];
+ return members.map((member) => ({
+ id: member.account.id,
+ name: member.account.name,
+ slug: member.account.slug,
+ imageUrl: member.account.imageUrl,
}));
};
@@ -61,6 +103,7 @@ const distinctBy = (callback) => (value, index, self) => {
const githubContributors = [];
const crowdinContributors = [];
+const opencollectiveContributors = [];
for (const { repository, slug } of sources.github) {
githubContributors.push(...(await fetchGithubContributors(slug, repository)));
@@ -77,3 +120,14 @@ for (const { projectId } of sources.crowdin) {
}
const distinctCrowdinContributors = crowdinContributors.filter(distinctBy((contributor) => contributor.username));
await fs.writeFile("./static-data/translators.json", JSON.stringify(distinctCrowdinContributors));
+
+for (const { slug } of sources.opencollective) {
+ opencollectiveContributors.push(...(await fetchOpenCollectiveContributors(slug)));
+}
+const distinctOpenCollectiveContributors = opencollectiveContributors.filter(
+ distinctBy((contributor) => contributor.slug),
+);
+await fs.writeFile(
+ "./static-data/opencollective-contributors.json",
+ JSON.stringify(distinctOpenCollectiveContributors),
+);
diff --git a/static-data/contributors.json b/static-data/contributors.json
index 79bf72e01..5db83ab61 100644
--- a/static-data/contributors.json
+++ b/static-data/contributors.json
@@ -1 +1 @@
-[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"tancak","avatar_url":"https://avatars.githubusercontent.com/u/11760749?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/u/47495914?v=4"},{"login":"bebsworthy","avatar_url":"https://avatars.githubusercontent.com/u/5266759?v=4"},{"login":"catrielmuller","avatar_url":"https://avatars.githubusercontent.com/u/2272323?v=4"},{"login":"Vysp3r","avatar_url":"https://avatars.githubusercontent.com/u/62019735?v=4"},{"login":"DR-Blogs","avatar_url":"https://avatars.githubusercontent.com/u/96393186?v=4"},{"login":"singular0","avatar_url":"https://avatars.githubusercontent.com/u/1925883?v=4"},{"login":"diogovalentte","avatar_url":"https://avatars.githubusercontent.com/u/49578155?v=4"},{"login":"codezninja","avatar_url":"https://avatars.githubusercontent.com/u/4400773?v=4"},{"login":"fabricionaweb","avatar_url":"https://avatars.githubusercontent.com/u/15933?v=4"},{"login":"nyok1912","avatar_url":"https://avatars.githubusercontent.com/u/32163949?v=4"},{"login":"bo3bdo","avatar_url":"https://avatars.githubusercontent.com/u/5271380?v=4"},{"login":"hskrtich","avatar_url":"https://avatars.githubusercontent.com/u/1214484?v=4"},{"login":"ishults","avatar_url":"https://avatars.githubusercontent.com/u/7275442?v=4"},{"login":"jelliuk","avatar_url":"https://avatars.githubusercontent.com/u/4790959?v=4"},{"login":"Moohan","avatar_url":"https://avatars.githubusercontent.com/u/5982260?v=4"},{"login":"tesar-tech","avatar_url":"https://avatars.githubusercontent.com/u/33880579?v=4"},{"login":"jbruell","avatar_url":"https://avatars.githubusercontent.com/u/31685172?v=4"},{"login":"JasonLeeB06","avatar_url":"https://avatars.githubusercontent.com/u/82266978?v=4"},{"login":"jeffersonraimon","avatar_url":"https://avatars.githubusercontent.com/u/80064475?v=4"},{"login":"jo-chemla","avatar_url":"https://avatars.githubusercontent.com/u/16822841?v=4"},{"login":"joser93","avatar_url":"https://avatars.githubusercontent.com/u/6912089?v=4"},{"login":"justijndepover","avatar_url":"https://avatars.githubusercontent.com/u/9008623?v=4"},{"login":"Aandree5","avatar_url":"https://avatars.githubusercontent.com/u/32734153?v=4"},{"login":"jackwellerreal","avatar_url":"https://avatars.githubusercontent.com/u/97435840?v=4"},{"login":"SergioChan","avatar_url":"https://avatars.githubusercontent.com/u/10103766?v=4"},{"login":"toineenzo","avatar_url":"https://avatars.githubusercontent.com/u/4921397?v=4"},{"login":"oben01","avatar_url":"https://avatars.githubusercontent.com/u/22369027?v=4"},{"login":"rezstje","avatar_url":"https://avatars.githubusercontent.com/u/45420315?v=4"},{"login":"castielwaverly","avatar_url":"https://avatars.githubusercontent.com/u/137005689?v=4"},{"login":"xoxyuxu","avatar_url":"https://avatars.githubusercontent.com/u/3543416?v=4"},{"login":"MaienM","avatar_url":"https://avatars.githubusercontent.com/u/583129?v=4"},{"login":"mvanhorn","avatar_url":"https://avatars.githubusercontent.com/u/455140?v=4"},{"login":"HeapReaper","avatar_url":"https://avatars.githubusercontent.com/u/31496522?v=4"},{"login":"Copilot","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},{"login":"mairas","avatar_url":"https://avatars.githubusercontent.com/u/407010?v=4"},{"login":"itzTheMeow","avatar_url":"https://avatars.githubusercontent.com/u/50887230?v=4"},{"login":"NicolasNewman","avatar_url":"https://avatars.githubusercontent.com/u/16135852?v=4"},{"login":"NikeLaosClericus","avatar_url":"https://avatars.githubusercontent.com/u/39810627?v=4"},{"login":"noahcroghan","avatar_url":"https://avatars.githubusercontent.com/u/27422774?v=4"},{"login":"ohmzi","avatar_url":"https://avatars.githubusercontent.com/u/6551272?v=4"},{"login":"Rhiz3K","avatar_url":"https://avatars.githubusercontent.com/u/33246262?v=4"},{"login":"saschabuehrle","avatar_url":"https://avatars.githubusercontent.com/u/47737812?v=4"},{"login":"StrandedTurtle","avatar_url":"https://avatars.githubusercontent.com/u/132835255?v=4"},{"login":"CrazyWolf13","avatar_url":"https://avatars.githubusercontent.com/u/96661824?v=4"},{"login":"generalmat82","avatar_url":"https://avatars.githubusercontent.com/u/110256073?v=4"},{"login":"lthomsen","avatar_url":"https://avatars.githubusercontent.com/u/28560325?v=4"},{"login":"pitschi","avatar_url":"https://avatars.githubusercontent.com/u/6841565?v=4"},{"login":"tech62","avatar_url":"https://avatars.githubusercontent.com/u/16237642?v=4"},{"login":"leandroscardua","avatar_url":"https://avatars.githubusercontent.com/u/24698541?v=4"},{"login":"kylegrieder","avatar_url":"https://avatars.githubusercontent.com/u/25424229?v=4"},{"login":"hotrungnhan","avatar_url":"https://avatars.githubusercontent.com/u/40029631?v=4"},{"login":"KelianB","avatar_url":"https://avatars.githubusercontent.com/u/11216155?v=4"},{"login":"Kaotic3","avatar_url":"https://avatars.githubusercontent.com/u/80522225?v=4"},{"login":"frankvHoof93","avatar_url":"https://avatars.githubusercontent.com/u/7346516?v=4"},{"login":"faugusto-oliveira","avatar_url":"https://avatars.githubusercontent.com/u/37384074?v=4"},{"login":"ieugen","avatar_url":"https://avatars.githubusercontent.com/u/1050920?v=4"},{"login":"KartulUdus","avatar_url":"https://avatars.githubusercontent.com/u/23346150?v=4"},{"login":"dgshue","avatar_url":"https://avatars.githubusercontent.com/u/12952156?v=4"},{"login":"DZ945brH","avatar_url":"https://avatars.githubusercontent.com/u/42635243?v=4"},{"login":"Oupsman","avatar_url":"https://avatars.githubusercontent.com/u/11260343?v=4"},{"login":"m0s4ik","avatar_url":"https://avatars.githubusercontent.com/u/26578395?v=4"},{"login":"AartSchinkel","avatar_url":"https://avatars.githubusercontent.com/u/189675191?v=4"}]
\ No newline at end of file
+[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/u/47495914?v=4"},{"login":"bebsworthy","avatar_url":"https://avatars.githubusercontent.com/u/5266759?v=4"},{"login":"catrielmuller","avatar_url":"https://avatars.githubusercontent.com/u/2272323?v=4"},{"login":"Vysp3r","avatar_url":"https://avatars.githubusercontent.com/u/62019735?v=4"},{"login":"DR-Blogs","avatar_url":"https://avatars.githubusercontent.com/u/96393186?v=4"},{"login":"singular0","avatar_url":"https://avatars.githubusercontent.com/u/1925883?v=4"},{"login":"diogovalentte","avatar_url":"https://avatars.githubusercontent.com/u/49578155?v=4"},{"login":"codezninja","avatar_url":"https://avatars.githubusercontent.com/u/4400773?v=4"},{"login":"fabricionaweb","avatar_url":"https://avatars.githubusercontent.com/u/15933?v=4"},{"login":"nyok1912","avatar_url":"https://avatars.githubusercontent.com/u/32163949?v=4"},{"login":"bo3bdo","avatar_url":"https://avatars.githubusercontent.com/u/5271380?v=4"},{"login":"hskrtich","avatar_url":"https://avatars.githubusercontent.com/u/1214484?v=4"},{"login":"ishults","avatar_url":"https://avatars.githubusercontent.com/u/7275442?v=4"},{"login":"jelliuk","avatar_url":"https://avatars.githubusercontent.com/u/4790959?v=4"},{"login":"Moohan","avatar_url":"https://avatars.githubusercontent.com/u/5982260?v=4"},{"login":"tesar-tech","avatar_url":"https://avatars.githubusercontent.com/u/33880579?v=4"},{"login":"jbruell","avatar_url":"https://avatars.githubusercontent.com/u/31685172?v=4"},{"login":"JasonLeeB06","avatar_url":"https://avatars.githubusercontent.com/u/82266978?v=4"},{"login":"jeffersonraimon","avatar_url":"https://avatars.githubusercontent.com/u/80064475?v=4"},{"login":"jo-chemla","avatar_url":"https://avatars.githubusercontent.com/u/16822841?v=4"},{"login":"joser93","avatar_url":"https://avatars.githubusercontent.com/u/6912089?v=4"},{"login":"justijndepover","avatar_url":"https://avatars.githubusercontent.com/u/9008623?v=4"},{"login":"Aandree5","avatar_url":"https://avatars.githubusercontent.com/u/32734153?v=4"},{"login":"jackwellerreal","avatar_url":"https://avatars.githubusercontent.com/u/97435840?v=4"},{"login":"SergioChan","avatar_url":"https://avatars.githubusercontent.com/u/10103766?v=4"},{"login":"toineenzo","avatar_url":"https://avatars.githubusercontent.com/u/4921397?v=4"},{"login":"oben01","avatar_url":"https://avatars.githubusercontent.com/u/22369027?v=4"},{"login":"rezstje","avatar_url":"https://avatars.githubusercontent.com/u/45420315?v=4"},{"login":"castielwaverly","avatar_url":"https://avatars.githubusercontent.com/u/137005689?v=4"},{"login":"xoxyuxu","avatar_url":"https://avatars.githubusercontent.com/u/3543416?v=4"},{"login":"MaienM","avatar_url":"https://avatars.githubusercontent.com/u/583129?v=4"},{"login":"mvanhorn","avatar_url":"https://avatars.githubusercontent.com/u/455140?v=4"},{"login":"HeapReaper","avatar_url":"https://avatars.githubusercontent.com/u/31496522?v=4"},{"login":"Copilot","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},{"login":"mairas","avatar_url":"https://avatars.githubusercontent.com/u/407010?v=4"},{"login":"itzTheMeow","avatar_url":"https://avatars.githubusercontent.com/u/50887230?v=4"},{"login":"NicolasNewman","avatar_url":"https://avatars.githubusercontent.com/u/16135852?v=4"},{"login":"NikeLaosClericus","avatar_url":"https://avatars.githubusercontent.com/u/39810627?v=4"},{"login":"noahcroghan","avatar_url":"https://avatars.githubusercontent.com/u/27422774?v=4"},{"login":"ohmzi","avatar_url":"https://avatars.githubusercontent.com/u/6551272?v=4"},{"login":"Rhiz3K","avatar_url":"https://avatars.githubusercontent.com/u/33246262?v=4"},{"login":"saschabuehrle","avatar_url":"https://avatars.githubusercontent.com/u/47737812?v=4"},{"login":"StrandedTurtle","avatar_url":"https://avatars.githubusercontent.com/u/132835255?v=4"},{"login":"CrazyWolf13","avatar_url":"https://avatars.githubusercontent.com/u/96661824?v=4"},{"login":"generalmat82","avatar_url":"https://avatars.githubusercontent.com/u/110256073?v=4"},{"login":"lthomsen","avatar_url":"https://avatars.githubusercontent.com/u/28560325?v=4"},{"login":"pitschi","avatar_url":"https://avatars.githubusercontent.com/u/6841565?v=4"},{"login":"tech62","avatar_url":"https://avatars.githubusercontent.com/u/16237642?v=4"},{"login":"leandroscardua","avatar_url":"https://avatars.githubusercontent.com/u/24698541?v=4"},{"login":"kylegrieder","avatar_url":"https://avatars.githubusercontent.com/u/25424229?v=4"},{"login":"hotrungnhan","avatar_url":"https://avatars.githubusercontent.com/u/40029631?v=4"},{"login":"KelianB","avatar_url":"https://avatars.githubusercontent.com/u/11216155?v=4"},{"login":"Kaotic3","avatar_url":"https://avatars.githubusercontent.com/u/80522225?v=4"},{"login":"frankvHoof93","avatar_url":"https://avatars.githubusercontent.com/u/7346516?v=4"},{"login":"faugusto-oliveira","avatar_url":"https://avatars.githubusercontent.com/u/37384074?v=4"},{"login":"ieugen","avatar_url":"https://avatars.githubusercontent.com/u/1050920?v=4"},{"login":"KartulUdus","avatar_url":"https://avatars.githubusercontent.com/u/23346150?v=4"},{"login":"dgshue","avatar_url":"https://avatars.githubusercontent.com/u/12952156?v=4"},{"login":"DZ945brH","avatar_url":"https://avatars.githubusercontent.com/u/42635243?v=4"},{"login":"Oupsman","avatar_url":"https://avatars.githubusercontent.com/u/11260343?v=4"},{"login":"m0s4ik","avatar_url":"https://avatars.githubusercontent.com/u/26578395?v=4"},{"login":"AartSchinkel","avatar_url":"https://avatars.githubusercontent.com/u/189675191?v=4"}]
\ No newline at end of file
diff --git a/static-data/opencollective-contributors.json b/static-data/opencollective-contributors.json
new file mode 100644
index 000000000..fcdf9d4da
--- /dev/null
+++ b/static-data/opencollective-contributors.json
@@ -0,0 +1 @@
+[{"id":"zaxon793-jy8gpl0v-d89pbrkd-emwl5v04","name":"Mike","slug":"guest-5da646e8","imageUrl":"https://images.opencollective.com/guest-5da646e8/avatar.png"},{"id":"ywz9j4av-god8pgwg-db36mr35-nxklb0e7","name":"Guest","slug":"guest-554ec68f","imageUrl":"https://images.opencollective.com/guest-554ec68f/avatar.png"},{"id":"mlo94zn7-x08dpo09-xlxpewga-3vjbrky5","name":"Robert Hermann","slug":"robert-hermann","imageUrl":"https://images.opencollective.com/robert-hermann/avatar.png"},{"id":"zaxon793-jy8gpl0n-8r3pbrkd-emwl5v04","name":"Sebastian Raubuch","slug":"sebastian-raubuch","imageUrl":"https://images.opencollective.com/sebastian-raubuch/avatar.png"},{"id":"nmlo94zn-7x08dpod-ykm6ewga-3vjbrky5","name":"Didier LUTHI","slug":"didier-luthi","imageUrl":"https://images.opencollective.com/didier-luthi/avatar.png"},{"id":"vedj9wro-z3a56dgk-z3z67blg-8x4m0ykn","name":"Guest","slug":"guest-b1459a68","imageUrl":"https://images.opencollective.com/guest-b1459a68/avatar.png"},{"id":"gm9bnk80-437xqry4-8vepvzeo-ljdayw5r","name":"Mohister","slug":"mohister","imageUrl":"https://images.opencollective.com/mohister/1d8a592/avatar.png"},{"id":"vrgbk357-l4x96e70-m0apomew-a0jdyzn8","name":"sidens","slug":"sidens","imageUrl":"https://images.opencollective.com/sidens/avatar.png"},{"id":"mlo94zn7-x08dpo0d-l7epewga-3vjbrky5","name":"Chris","slug":"guest-937065bb","imageUrl":"https://images.opencollective.com/guest-937065bb/avatar.png"},{"id":"k9mbw7y4-8r3zq3ol-7zlq0ej5-lavnodgx","name":"Nick W","slug":"guest-7501840a","imageUrl":"https://images.opencollective.com/guest-7501840a/avatar.png"},{"id":"e0adkjrv-8xwm69o8-5al67z5n-4l93bgoy","name":"Jeppedy","slug":"guest-ab78e46a","imageUrl":"https://images.opencollective.com/guest-ab78e46a/avatar.png"},{"id":"a47byg9n-xozdp8xo-jgm6mjlv-03rek5w8","name":"naru","slug":"guest-02fd264a","imageUrl":"https://images.opencollective.com/guest-02fd264a/avatar.png"},{"id":"3k0exgzn-m8yj64g3-ll865wao-9r7b4dlv","name":"Jared Hocutt","slug":"jared-hocutt","imageUrl":"https://images.opencollective.com/jared-hocutt/avatar.png"},{"id":"eng0kzdy-vor4pz8l-exopbma8-37xlw95j","name":"Incognito","slug":"user-e63f4a5b","imageUrl":"https://images.opencollective.com/user-e63f4a5b/avatar.png"},{"id":"8k03reyd-5agmq50v-8vyplbwo-z7j4nxv9","name":"dazi","slug":"guest-11663a46","imageUrl":"https://images.opencollective.com/guest-11663a46/avatar.png"},{"id":"vedj9wro-z3a56dgd-zwl67blg-8x4m0ykn","name":"ZoopaMario","slug":"zoopamario","imageUrl":"https://images.opencollective.com/zoopamario/bea900d/avatar.png"},{"id":"a47byg9n-xozdp8xe-xmb6mjlv-03rek5w8","name":"FlamebergUA","slug":"guest-a59fd500","imageUrl":"https://images.opencollective.com/guest-a59fd500/avatar.png"},{"id":"8k03reyd-5agmq50y-kzoplbwo-z7j4nxv9","name":"StLCards","slug":"guest-e1de33dc","imageUrl":"https://images.opencollective.com/guest-e1de33dc/avatar.png"},{"id":"ejoxl3az-45w9pa54-dk46y870-mgkbrvdn","name":"Varadha","slug":"guest-6f68eed9","imageUrl":"https://images.opencollective.com/guest-6f68eed9/avatar.png"},{"id":"jrkx5lmn-v904qjl0-3oz68bwa-7zdygoe3","name":"Guest","slug":"guest-6da419b5","imageUrl":"https://images.opencollective.com/guest-6da419b5/avatar.png"},{"id":"mywxoz34-09rl6k3z-lmlqvenb-dj7gk85a","name":"Guest","slug":"guest-122d7e0a","imageUrl":"https://images.opencollective.com/guest-122d7e0a/avatar.png"},{"id":"3z8arxve-ymko60vm-3eaqgl5n-bj9w704d","name":"Ricardo","slug":"ricardo34","imageUrl":"https://images.opencollective.com/ricardo34/avatar.png"},{"id":"e0adkjrv-8xwm69or-reo67z5n-4l93bgoy","name":"GoM","slug":"guest-3944feab","imageUrl":"https://images.opencollective.com/guest-3944feab/avatar.png"},{"id":"gm9bnk80-437xqrnr-r5o6vzeo-ljdayw5r","name":"Brent Maxwell","slug":"brentmaxwell","imageUrl":"https://images.opencollective.com/brentmaxwell/avatar.png"},{"id":"gnxdwzj3-le5mpwzd-ll0py8rv-bko04a97","name":"Eric G","slug":"guest-478270f1","imageUrl":"https://images.opencollective.com/guest-478270f1/avatar.png"},{"id":"v349mrwg-z75lpyjo-ez8pa08d-jeybknox","name":"ninja","slug":"guest-0bb6ba36","imageUrl":"https://images.opencollective.com/guest-0bb6ba36/avatar.png"},{"id":"ywz9j4av-god8pgw0-53y6mr35-nxklb0e7","name":"s11","slug":"guest-6e9a2588","imageUrl":"https://images.opencollective.com/guest-6e9a2588/avatar.png"},{"id":"mlo94zn7-x08dpo30-v73pewga-3vjbrky5","name":"Incognito","slug":"user-cbad11af","imageUrl":"https://images.opencollective.com/user-cbad11af/avatar.png"},{"id":"v349mrwg-z75lpy7l-yzgpa08d-jeybknox","name":"Ronald Simmons","slug":"ronald-simmons","imageUrl":"https://images.opencollective.com/ronald-simmons/avatar.png"},{"id":"7ywz9j4a-vgod8pgl-me9qmr35-nxklb0e7","name":"Joel","slug":"guest-3376ab02","imageUrl":"https://images.opencollective.com/guest-3376ab02/avatar.png"},{"id":"v349mrwg-z75lpyjw-kxzpa08d-jeybknox","name":"Gokay Yamankurt","slug":"gokay-yamankurt","imageUrl":"https://images.opencollective.com/gokay-yamankurt/avatar.png"},{"id":"v349mrwg-z75lpyjv-59rpa08d-jeybknox","name":"Rhiz3K","slug":"rhiz3k","imageUrl":"https://images.opencollective.com/rhiz3k/7287acd/avatar.png"},{"id":"8k03reyd-5agmq5x7-77gplbwo-z7j4nxv9","name":"vukie","slug":"guest-40aabe13","imageUrl":"https://images.opencollective.com/guest-40aabe13/avatar.png"},{"id":"a47byg9n-xozdp84a-rmvqmjlv-03rek5w8","name":"Guest","slug":"guest-2fe979fa","imageUrl":"https://images.opencollective.com/guest-2fe979fa/avatar.png"},{"id":"rxg0j35l-zkwm6vgm-k3x6voe9-8n47daby","name":"AngryPig","slug":"guest-b9e8a062","imageUrl":"https://images.opencollective.com/guest-b9e8a062/avatar.png"}]
\ No newline at end of file
diff --git a/static-data/translators.json b/static-data/translators.json
index 7a78e8b94..acb321d6e 100644
--- a/static-data/translators.json
+++ b/static-data/translators.json
@@ -1 +1 @@
-[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14934947/medium/f5a8570713c34ab0f7d5405d105e2a9a.jpeg"},{"username":"f1refa11","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14949159/medium/fd2ae63b8eb4462200ba96abf943c1b9.png"},{"username":"jeffersonraimon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057621/medium/6dff469563860e2618bc9d45646d0ab4.png"},{"username":"giop98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057987/medium/b8a4825d3fc39fc662f35ee258db4b2d.jpeg"},{"username":"tomislav.kraljevic","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15118857/medium/e133f1061cc92850b854d05d8faaeafd.png"},{"username":"Walkx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15202182/medium/5c37361ae45aeed487b34582c1f7ca37.png"},{"username":"andrea.rosso","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15246318/medium/11f3f5ef44ec7f55b6f143090e208704_default.png"},{"username":"ajnart","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15270132/medium/a0f107a463c8910ee96bc2fa843a17e3.jpeg"},{"username":"Witchling","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15282238/medium/9e196b6783415b10ca571b7673c2ef33.jpg"},{"username":"guineuu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15292058/medium/22fbb758bda3b7805d50bf21d38f2c20.jpeg"},{"username":"Bulgus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15304568/medium/0e8787e5ceb02ed5c96a514d0068ae87.jpg"},{"username":"Narno","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15314996/medium/a6ba093f24884250a1bf6c6f2ca50f1b_default.png"},{"username":"BySempron","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15315986/medium/e3d22d7b1423c6823a9f36d595ed4bdb.png"},{"username":"jonathan.berglin.work","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15359236/medium/95930b2093db13b76179782f7322c5d5.png"},{"username":"icdmkg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405504/medium/f334d16cde1ac4ffb935b440d03465af.gif"},{"username":"irithys","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405614/medium/3086461c47cce0a0c031925e5f943412.png"},{"username":"JannesV","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419912/medium/a7809eb4b817d7c49b62cf10ae86b950.png"},{"username":"benniblot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419914/medium/8086b1573d31f152ba41949b3a172445.jpeg"},{"username":"pacjo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419916/medium/3cbeddbd7bc01faafb5a3bf47bba915b_default.png"},{"username":"Payou6994","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419934/medium/37c9b9b17dfb578404c1c1ddb73ba7a8.png"},{"username":"fzibi21","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419976/medium/0da688450358e0290a7b7359cc1f7328.png"},{"username":"WowMurdock","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420118/medium/de19576fccb495b6dfe4c4c04a56b834.png"},{"username":"hbooo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420120/medium/6c7c7f8db785061356ebb03d044d3329.jpeg"},{"username":"Manicraft1001","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420178/medium/50ec94563a06a9f74f33bd09f01eed4d.jpg"},{"username":"Void123","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420354/medium/86929d44df92a00f9fe900a985c196df_default.png"},{"username":"R4cc","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15422606/medium/a390979662b84694f59de30bdb732141.jpeg"},{"username":"fabricionaweb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15425808/medium/a9354142e7ae5152c144409d55fda551_default.png"},{"username":"JokeOfDead","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15426890/medium/a383eac9365e9de64fd4ab1d6fd0cb95.jpeg"},{"username":"hkz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15427174/medium/c88acefb0d7306e1f7470e872029fb39_default.png"},{"username":"Steken","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428516/medium/d5aea5653c769c3a523182bdb60d1664.png"},{"username":"flar.anton","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428592/medium/b01fdb365d892e9f811f77fcb50a80a2.jpeg"},{"username":"Bon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15433542/medium/5397da4dfc821f20b6ac14fe0c514e9a.jpeg"},{"username":"bfkadan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434162/medium/4f9a0b43cfe3acaea60124c14ba7f44a.png"},{"username":"BunnySweety","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434662/medium/f0ef200a6a0dcf0e1d0e9ecd4148f560_default.png"},{"username":"HRKings","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15436168/medium/69a31e461d38549f01864e7ef10d642e.png"},{"username":"wiston81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15439078/medium/10f292c8d12a7c21a44b54495fa4a3d8.jpeg"},{"username":"qaz0911","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15440860/medium/3d3e50ee388c72dc4bf7a771761f2d89_default.png"},{"username":"qqyule","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15441462/medium/7a3cdf82710ffb5d8f388bc0bd010665.png"},{"username":"Bims0n","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15445560/medium/603220b603eeb3367e0f0d3fa675247c.jpg"},{"username":"TariqDaCoder","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15446228/medium/50b0f4040112bbd67690b769477398e5_default.png"},{"username":"Anarchon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15449644/medium/e925e1f3e3ffbf0f982391ce263a1a28.jpeg"},{"username":"Ashun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15453020/medium/ccdcf51c73d6aae40751bb30beee1915_default.png"},{"username":"sebekmartin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15454038/medium/bcfb44598cdfd1d7cd4eb35812538962.jpeg"},{"username":"RagnarGraves","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15459882/medium/143d5af850c1154070a218bea124e9cb_default.png"},{"username":"PrtmPhlp","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15462414/medium/b80db55e9de301432dcd1f8c8b24fd49_default.png"},{"username":"DooYoo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15470768/medium/2a18cf4be67094724b508c9e1e698a21_default.png"},{"username":"espentruls","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486092/medium/8e38afc3a4ff669226a0cfd3e420ff3a.jpeg"},{"username":"frisco82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486922/medium/4e1c5d4189b42508e660daa3c1c25b2a.jpeg"},{"username":"lupineDK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15491798/medium/461bd501e8371c062bf29ea171aedd36_default.png"},{"username":"hillaliy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15492732/medium/0bae17b421604892d888e3fc70cf0587.jpeg"},{"username":"MarcOrfilaCarreras","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15501072/medium/ea52b26c3c6f21e4931e38e3ce3f3d6e.png"},{"username":"robertbridda","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15507822/medium/a368c2e30411bb2da9b49290084191f3.png"},{"username":"MKoniuszko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518090/medium/87605434fcc839f6763ab07c50f6d232.jpeg"},{"username":"HeroSizy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518710/medium/4e79c0e98cbeb536dd961e656331b509.png"},{"username":"dwt136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15520022/medium/554422503f2baea43ace85facb4546fb_default.png"},{"username":"asifthewebguy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15526719/medium/b18931dd0c800d725048bd440646198b_default.png"},{"username":"eiloogs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15545537/medium/f290a2f1190983530a9b76b2e858a609.gif"},{"username":"_vytdv","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15547289/medium/60d8644cc8ad6f11a92ccea4a14cd098_default.png"},{"username":"crendasien","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15554645/medium/598ab1d4aaf6b8dccd5ba16be92da7b9.jpeg"},{"username":"edxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15573823/medium/8a565b732a75a77f840dd123cdb30bf4.png"},{"username":"somerlev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15617065/medium/f4b13513e311ec902d90b2f718412c55.jpg"},{"username":"kid1412621","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15643771/medium/dd455e32de652fa88e6fd97598bdffa7.png"},{"username":"suming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15644717/medium/244159dfe10fa03436205506f80c9e25.png"},{"username":"nick.gher","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15647517/medium/5374a2c6fef60a3fbf0edf86b997c351_default.png"},{"username":"DataCat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15650315/medium/ce7c1365adf35c5d490d77500a4607fb_default.png"},{"username":"dizo89","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15658375/medium/0ca745e5017d491fe1b22b0239904de8.jpeg"},{"username":"tee_noodle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674577/medium/804d9cf06f5196026acb6436b809d0da_default.png"},{"username":"Marty88","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674593/medium/492b1509d52bd2809dea768121217125.jpeg"},{"username":"Spillebulle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15677023/medium/20b98bb85062e07afe0e63618f8a5feb.PNG"},{"username":"petitmewen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15685239/medium/15de9b62d2e0bc25013435f1784bbcc1.jpeg"},{"username":"NoProsNoNoobs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15687709/medium/ae8f65fafeb8bcf74dcb8871bbe46461.png"},{"username":"y.gybson","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15690777/medium/b5cb5d2d5768904ff6586c727e3a6c77.jpeg"},{"username":"inside90","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15704947/medium/c1355fcb30dd76f8e39d98d1d49f1c52.png"},{"username":"RJSkudra","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15709853/medium/c3abf2774913dc4e81fb261d36d7668c.png"},{"username":"binge203","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15713937/medium/77c1cfa4314673db80e6881fd9f64668.gif"},{"username":"Mailootje","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15714337/medium/743c3bc4ab1989966a375eeeec83d8b8.jpeg"},{"username":"tim-wiegers","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15719805/medium/08a96f064813350661cd6b20bf3d7d99.png"},{"username":"GkhnG","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15722911/medium/71a027caec489ef6ce82bcf1888329d0_default.png"},{"username":"Zoen-Millo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15760967/medium/9e956f11adc5b34f5636268b5c485dbf.jpg"},{"username":"HooinKyoma","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15792897/medium/9489f0a9b368e0e827ae758b740a2eed.jpeg"},{"username":"wathergeen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15794495/medium/a83b7fc80d180669ebfd6deb3544cfd0.png"},{"username":"MoeToo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15818233/medium/d35cd6953717706eaf20f6c143c62947.png"},{"username":"itodouble","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15836233/medium/e984caea18fb0673bf319fcf28cef649.png"},{"username":"loslocitos","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15847901/medium/fe30d12fd2cf38212f929e13b169f9ec.jpeg"},{"username":"Oversleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15849065/medium/06141f13a6d541d753f3c2f2947b8068_default.png"},{"username":"Beardy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15865139/medium/fca6b9d2b3f52e286d1568f52b83b6a0_default.png"},{"username":"raelyan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15875457/medium/2f4fda1d1aaa5dcc79b328baf3f03151.jpeg"},{"username":"dolphin738","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15913763/medium/ee6fede7b8528ca642329ada80d1cc18.png"},{"username":"brunotco","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15916719/medium/09db45880fc05abc18adb8d932a5ecf9_default.png"},{"username":"LukeIsHereToDevelop","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15924739/medium/3e6cd3940297bb4ba7bb039c93e6f918.png"},{"username":"kennit","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15925879/medium/6b0733ad3c5949b91c55e4d8b03db8a5_default.png"},{"username":"kuunpire","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15950309/medium/8192a4f08f07086828ac9f74ed29a169.jpeg"},{"username":"Sandor-dev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15951759/medium/0216c2da4eb028164ebbecf1c72f6271_default.png"},{"username":"Meierschlumpf","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15953187/medium/23c744faef1ab84fbdc9351a7850aab6.jpeg"},{"username":"harmlesscat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15970733/medium/dd8d0214a0250c932bb518b1b55e45a2_default.png"},{"username":"OrzWTF","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15976121/medium/4c4557cbff7ff7b0503455bc59c020e0.jpeg"},{"username":"tagaishi","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15977271/medium/eade504c83a5a1ff831c80a538fbdb44_default.png"},{"username":"azurite928","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15981895/medium/547ccc459ee123e78b5401c499f1022d.png"},{"username":"Ronner231","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16021342/medium/7734d550df2de5a2fec2ffff33e7024c.jpeg"},{"username":"ugyes","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16034148/medium/ed001e3f470a2dea9a8ce955b18e7bd5.png"},{"username":"rpieja","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16045554/medium/bd55baca2ef8b92502a760cc9ee7c505_default.png"},{"username":"flambyisyou","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16051620/medium/3a3bc0c90f6b95ab4ef74396a0a17beb.png"},{"username":"Topbcy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16077170/medium/d3aed33ea56330338756cfcd89477cfe.jpeg"},{"username":"ai5d02sb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16084674/medium/7c8119fe2a5ca71bb15f636916a42b95_default.png"},{"username":"explosiveparrot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16097722/medium/7762f80fc1da63f5b2eb87de9d640324_default.png"},{"username":"DestroyCom","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16111544/medium/83e3d3ac5c8bfed634cd1a074854db67.png"},{"username":"neotraxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16116966/medium/b30df957873329019a8a90a69f6efe3a.png"},{"username":"gzxpa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16123314/medium/487b05577310a946b0b98bc72c11213b_default.png"},{"username":"TORIK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16124732/medium/de770890e6411860ac06e8cc2dc0abd0_default.png"},{"username":"sergio.pibot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16138114/medium/80db802b2fcd6bbf19ea4c7413367dca.jpeg"},{"username":"karam-ajaj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16139838/medium/7539ab958d3c85d5f67e7849483ea8f7.jpeg"},{"username":"SimonHenz97","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16151520/medium/9b94a85b536688e2d94538c384fd0a76.png"},{"username":"olsson82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16198988/medium/eeaab4cba210c537424d4204754b604e.png"},{"username":"bo3bdo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16222806/medium/8652f3beacb6098dde5a4cd0a29fe80d.jpeg"},{"username":"AngryPlayer04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245192/medium/342715274f4dc31fd4132cdae9126188.jpeg"},{"username":"ehatamm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245884/medium/979d5e5526fe545be9bf46db481f7182.jpeg"},{"username":"Balionelis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16246058/medium/986d62ba363594c517d966fe9178dcbb.png"},{"username":"kwargs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16248816/medium/19ce11f04bf1ae2637e0483399c675f9_default.png"},{"username":"lucicu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16273892/medium/3b09e8f3bead221ef98d6a45a16851dc_default.png"},{"username":"Michael5564445","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16357688/medium/9d9026a8bf3eaccb0417da240932ebf7.png"},{"username":"Netscream","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16359198/medium/694e0c8572dc8a47e6d63da31ec5b992.png"},{"username":"belicstefan04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16368142/medium/4742dcfd7bf7ad7e33e37ac44fba7e0f.png"},{"username":"Hamad2066","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16371478/medium/f37bbc7685b6757defdbd1494236af61.jpeg"},{"username":"3ct0pl4sm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16399616/medium/ce796d1c40163975624c0a483e56374f.png"},{"username":"sanchez.marcell626","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16433865/medium/3ddde1314764cc2205ffdcd1ae719a91.png"},{"username":"Minoctis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16490079/medium/91fd0849303a4eca033d660d0a7b6854.jpeg"},{"username":"vittoriosoccorso","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16498805/medium/17086b2f1981f734edd1572f79c3158a.jpeg"},{"username":"homarr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16657003/medium/3afabfd3de949a54ff5d40ad04552aba.png"},{"username":"mwgg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16658379/medium/763dd09f5c238cc5324d4d5357985292.png"},{"username":"kiwinho","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16671855/medium/bfe3a2d497dc4748a04d02e4ae2139cb.jpeg"},{"username":"tjvg4m34r13","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16679135/medium/7a483b9b0abe0d925e54808e1cb38fa0.jpeg"},{"username":"Diegoem","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16705921/medium/29ce68ebc191fc3f65499511b12de98f_default.png"},{"username":"bouks69","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16746091/medium/b58b1fc08520de416198be9df5d093eb.jpeg"},{"username":"futhgar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16748067/medium/b8c526b097f9aa9d687250cac3df16dc.jpeg"},{"username":"GrimSQL","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16764343/medium/fff729a76d1f8fbcdaee9bb0c0f5f3fd.png"},{"username":"chnikel","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767047/medium/3dda2a37e03049dafefe6697bb00f75c.png"},{"username":"yuki19870616","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767951/medium/f59c848e38382cda6c8464b0a7b9e642_default.png"},{"username":"noamdal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16782245/medium/b4a72a61f9caf138f0322c0cb987e6d0.png"},{"username":"MalithRukshan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16809057/medium/084d6a5b35fbbeacc70e7021197d222f.png"},{"username":"minirope19","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16930929/medium/e8cbbc26747e31c5a8959215e1dd067b.jpeg"},{"username":"prubin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17503492/medium/96ac87644691e49c1f983f2f094e541c_default.png"},{"username":"sam-tam-sam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17578052/medium/1628dc2954ce3ae95e0caaa2bd60ab0c.png"},{"username":"toineenzo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12371852/medium/9f1b2bedb4794c59479cd289bdba9fab.png"},{"username":"ArcaneWater","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12421105/medium/1ac82517a6c658f6b62c48aebcb17e86_default.png"},{"username":"Ebedami","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12974801/medium/b596dea5c09454c4c828eac558389755.jpg"},{"username":"perrygamerptyt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13117177/medium/1406c949507ecd438480473c8a1f6124.jpg"},{"username":"SergiSvK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13320543/medium/3df3c79cc5f0c53716c559cd3fd2fc20.png"},{"username":"jeongeon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13362856/medium/eb572f72cde5e5526c517ca3a6489850_default.png"},{"username":"MrTranCFCVN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13439475/medium/a1789d69c85d90c667194b5d6a518c08_default.png"},{"username":"Tobost06","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13467449/medium/9227dc07f6366bc060d28304f5cb8ce7.jpeg"},{"username":"Kf637","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13530622/medium/118b00bb72d01d3229c97409637e25d1_default.png"},{"username":"Cruellest","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13610615/medium/2a74faa0947ee08d18f7946fb8602c1e.jpg"},{"username":"Thario","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14491372/medium/dd5e142070802a672900121aa3f332a6_default.png"},{"username":"Wjouter","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14585620/medium/b91a51513332bdc0a0a9ecbed99ecbc3.png"},{"username":"albanobattistella","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14686194/medium/564afcfd76f5cd24751bd669cf4943aa.jpg"},{"username":"Kachelkaiser","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14763406/medium/203a781797da4a18fe0f4e7db8fd547c.png"},{"username":"regi4","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14842936/medium/f7e1d672f562a7db61ba6b4b8f4de41d.png"},{"username":"baton5129","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14891064/medium/8d438f557b82977dd02f0ead3187265f.PNG"},{"username":"meviper","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15958903/medium/209f521ce33a05831fa8d7d1b7babc20_default.png"},{"username":"GerOriki","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16268376/medium/da00e54b08cd40348231d92873a0cd0a_default.png"},{"username":"TheRedCyclops","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16486399/medium/7f39226180120d5a9efcb8e13d8aec8e_default.png"},{"username":"dymek37","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16659221/medium/d1dc6d11d7e7fc370efc5070e837ae03.png"},{"username":"B3CKDOOR","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16744975/medium/e6c7b2d45e76465d54e6efb67c83da7d.png"},{"username":"Larsluph","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16779933/medium/ed7d36f8b44013bad3a16ac0491bd0f7.png"},{"username":"FOEDI","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16781001/medium/6b4552fb72e359aa016f7a054078fac8.png"},{"username":"xmarkurion","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16805443/medium/34a20428427de5aaef99c7d140646803.png"},{"username":"gregid","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16806705/medium/f3c8f4a20436e8538929130142bbc104.png"},{"username":"Flameberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16820453/medium/d47be866148474c9d05fec6fbe406d88.png"},{"username":"Playfust","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16872865/medium/5f4609cc711e5fb9fa8a0b178635769c.jpeg"},{"username":"Cotignac","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16879297/medium/81ff353edf030699a159bb79cda26e84.jpg"},{"username":"LeaHun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16916375/medium/618520f0543b1e212ebc83d66b683d4b_default.png"},{"username":"jackwellerreal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16919717/medium/37bbfdc03893f141133ccc619593266d.png"},{"username":"andrius.cim","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16967845/medium/46028fc1a0ddfaaeebcb37b39739625d_default.png"},{"username":"xjxexrxrxyx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17041412/medium/d34d664166db6fb25579bca9ff3b50b4.jpeg"},{"username":"bilhal.fellah","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17044572/medium/d1ec6f17e4aa013e9eededc83778d2c6.png"},{"username":"the_octo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17058060/medium/7423ab3a4cabb67f5decb2e2dfa2b675_default.png"},{"username":"BamesJond","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17075846/medium/128d7ffd40254c8e376f45678e2fc620_default.png"},{"username":"Kornikus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17134240/medium/d7a42b2c30db46ff152699ca01f9e11a_default.png"},{"username":"xoxyuxu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17176526/medium/108055aa24343043de852e412bec74be.png"},{"username":"cryptodntsleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17249408/medium/b9e15aa2b24a0dbc1e4f194aa8ed5109.png"},{"username":"yaniorg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17261842/medium/7dbfc77f21c826845961243c44540d26.jpg"},{"username":"Vidariondr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17262438/medium/45fbc71be02ef27296b6f423e97b6a03_default.png"},{"username":"Fastery","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17263066/medium/701b0edc59676174863ad651528bbb37.jpeg"},{"username":"stratself","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17290388/medium/57d9d8161b3fd0abf4dc5558b4a1fa05_default.png"},{"username":"wuzeqin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17292606/medium/b9e0308410b31337e2d7aa6ed346d718_default.png"},{"username":"ChristopherBull","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17304338/medium/21a175ce596bfae81430767467908b90.jpeg"},{"username":"darkbcnlord","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17311436/medium/b271bc0b419ec3a26ddb7b63ff8ffc4b.png"},{"username":"zhoran1997","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17344028/medium/c8f450b5a8f1e9494a45dc60af7ce63e.png"},{"username":"gilalves1703","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17447768/medium/3f1de562a5c2391c88bbf8d5cf40a266.png"},{"username":"iServeCloud","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17470960/medium/11649b99d609438d66455a4d2fdc70f7.png"},{"username":"guibolzantvbox","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17474136/medium/59a576e6da538664ceb972253916fa02.jpeg"},{"username":"ivann136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17506062/medium/67d09ada36fac5f444a1f7517e3a97bd_default.png"},{"username":"Maheshivara","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17516082/medium/576e1e6581cf969615b265de6aeff8b7.jpeg"},{"username":"brunogui81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17517542/medium/7c0d174e810435af518fe8e0c05e151f.jpeg"},{"username":"michelwilhelm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17542190/medium/4c6b33fd8993ff565fa75add2ce8355e.jpeg"},{"username":"dias-gxstavo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17547376/medium/8c6b109aae8aeb651192346a39ae9e5b.jpeg"},{"username":"ToxykAuBleu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17614030/medium/8d21e0608560172b121bd46126d827c7.jpeg"},{"username":"e.erdemir","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17617994/medium/7259a29db9941c6171a8cbbd643b92bd.png"},{"username":"sjonkreutz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17618306/medium/7601c45d015189a863656e21348d223b.png"},{"username":"volovich0377","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17620388/medium/c929a8fbd24f96d17620fa497ade7309.png"},{"username":"Hamstio1","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17624898/medium/406e0bf40094c24a01c3064f8f5b2a82.png"},{"username":"i3laa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17627462/medium/613a9d283ed8b69819ee181903851660.jpeg"},{"username":"bastiengrignon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17629884/medium/ddfc3339285bd65195fcd834e512e9b7.jpeg"},{"username":"kauhee","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17644600/medium/def1b9066cf9275b4273a5a5dca7f325.png"}]
\ No newline at end of file
+[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14934947/medium/f5a8570713c34ab0f7d5405d105e2a9a.jpeg"},{"username":"f1refa11","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14949159/medium/fd2ae63b8eb4462200ba96abf943c1b9.png"},{"username":"jeffersonraimon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057621/medium/6dff469563860e2618bc9d45646d0ab4.png"},{"username":"giop98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057987/medium/b8a4825d3fc39fc662f35ee258db4b2d.jpeg"},{"username":"tomislav.kraljevic","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15118857/medium/e133f1061cc92850b854d05d8faaeafd.png"},{"username":"Walkx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15202182/medium/5c37361ae45aeed487b34582c1f7ca37.png"},{"username":"andrea.rosso","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15246318/medium/11f3f5ef44ec7f55b6f143090e208704_default.png"},{"username":"ajnart","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15270132/medium/a0f107a463c8910ee96bc2fa843a17e3.jpeg"},{"username":"Witchling","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15282238/medium/9e196b6783415b10ca571b7673c2ef33.jpg"},{"username":"guineuu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15292058/medium/22fbb758bda3b7805d50bf21d38f2c20.jpeg"},{"username":"Bulgus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15304568/medium/0e8787e5ceb02ed5c96a514d0068ae87.jpg"},{"username":"Narno","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15314996/medium/a6ba093f24884250a1bf6c6f2ca50f1b_default.png"},{"username":"BySempron","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15315986/medium/e3d22d7b1423c6823a9f36d595ed4bdb.png"},{"username":"jonathan.berglin.work","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15359236/medium/95930b2093db13b76179782f7322c5d5.png"},{"username":"icdmkg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405504/medium/f334d16cde1ac4ffb935b440d03465af.gif"},{"username":"irithys","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405614/medium/3086461c47cce0a0c031925e5f943412.png"},{"username":"JannesV","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419912/medium/a7809eb4b817d7c49b62cf10ae86b950.png"},{"username":"benniblot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419914/medium/8086b1573d31f152ba41949b3a172445.jpeg"},{"username":"pacjo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419916/medium/3cbeddbd7bc01faafb5a3bf47bba915b_default.png"},{"username":"Payou6994","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419934/medium/37c9b9b17dfb578404c1c1ddb73ba7a8.png"},{"username":"fzibi21","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419976/medium/0da688450358e0290a7b7359cc1f7328.png"},{"username":"WowMurdock","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420118/medium/de19576fccb495b6dfe4c4c04a56b834.png"},{"username":"hbooo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420120/medium/6c7c7f8db785061356ebb03d044d3329.jpeg"},{"username":"Manicraft1001","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420178/medium/50ec94563a06a9f74f33bd09f01eed4d.jpg"},{"username":"Void123","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420354/medium/86929d44df92a00f9fe900a985c196df_default.png"},{"username":"R4cc","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15422606/medium/a390979662b84694f59de30bdb732141.jpeg"},{"username":"fabricionaweb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15425808/medium/a9354142e7ae5152c144409d55fda551_default.png"},{"username":"JokeOfDead","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15426890/medium/a383eac9365e9de64fd4ab1d6fd0cb95.jpeg"},{"username":"hkz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15427174/medium/c88acefb0d7306e1f7470e872029fb39_default.png"},{"username":"Steken","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428516/medium/d5aea5653c769c3a523182bdb60d1664.png"},{"username":"flar.anton","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428592/medium/b01fdb365d892e9f811f77fcb50a80a2.jpeg"},{"username":"Bon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15433542/medium/5397da4dfc821f20b6ac14fe0c514e9a.jpeg"},{"username":"bfkadan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434162/medium/4f9a0b43cfe3acaea60124c14ba7f44a.png"},{"username":"BunnySweety","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434662/medium/f0ef200a6a0dcf0e1d0e9ecd4148f560_default.png"},{"username":"HRKings","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15436168/medium/69a31e461d38549f01864e7ef10d642e.png"},{"username":"wiston81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15439078/medium/10f292c8d12a7c21a44b54495fa4a3d8.jpeg"},{"username":"qaz0911","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15440860/medium/3d3e50ee388c72dc4bf7a771761f2d89_default.png"},{"username":"qqyule","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15441462/medium/7a3cdf82710ffb5d8f388bc0bd010665.png"},{"username":"Bims0n","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15445560/medium/603220b603eeb3367e0f0d3fa675247c.jpg"},{"username":"TariqDaCoder","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15446228/medium/50b0f4040112bbd67690b769477398e5_default.png"},{"username":"Anarchon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15449644/medium/e925e1f3e3ffbf0f982391ce263a1a28.jpeg"},{"username":"Ashun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15453020/medium/ccdcf51c73d6aae40751bb30beee1915_default.png"},{"username":"sebekmartin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15454038/medium/bcfb44598cdfd1d7cd4eb35812538962.jpeg"},{"username":"RagnarGraves","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15459882/medium/143d5af850c1154070a218bea124e9cb_default.png"},{"username":"PrtmPhlp","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15462414/medium/b80db55e9de301432dcd1f8c8b24fd49_default.png"},{"username":"DooYoo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15470768/medium/2a18cf4be67094724b508c9e1e698a21_default.png"},{"username":"espentruls","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486092/medium/8e38afc3a4ff669226a0cfd3e420ff3a.jpeg"},{"username":"frisco82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486922/medium/4e1c5d4189b42508e660daa3c1c25b2a.jpeg"},{"username":"lupineDK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15491798/medium/461bd501e8371c062bf29ea171aedd36_default.png"},{"username":"hillaliy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15492732/medium/0bae17b421604892d888e3fc70cf0587.jpeg"},{"username":"MarcOrfilaCarreras","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15501072/medium/ea52b26c3c6f21e4931e38e3ce3f3d6e.png"},{"username":"robertbridda","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15507822/medium/a368c2e30411bb2da9b49290084191f3.png"},{"username":"MKoniuszko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518090/medium/87605434fcc839f6763ab07c50f6d232.jpeg"},{"username":"HeroSizy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518710/medium/4e79c0e98cbeb536dd961e656331b509.png"},{"username":"dwt136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15520022/medium/554422503f2baea43ace85facb4546fb_default.png"},{"username":"asifthewebguy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15526719/medium/b18931dd0c800d725048bd440646198b_default.png"},{"username":"eiloogs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15545537/medium/f290a2f1190983530a9b76b2e858a609.gif"},{"username":"_vytdv","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15547289/medium/60d8644cc8ad6f11a92ccea4a14cd098_default.png"},{"username":"crendasien","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15554645/medium/598ab1d4aaf6b8dccd5ba16be92da7b9.jpeg"},{"username":"edxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15573823/medium/8a565b732a75a77f840dd123cdb30bf4.png"},{"username":"somerlev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15617065/medium/f4b13513e311ec902d90b2f718412c55.jpg"},{"username":"kid1412621","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15643771/medium/dd455e32de652fa88e6fd97598bdffa7.png"},{"username":"suming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15644717/medium/244159dfe10fa03436205506f80c9e25.png"},{"username":"nick.gher","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15647517/medium/5374a2c6fef60a3fbf0edf86b997c351_default.png"},{"username":"DataCat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15650315/medium/ce7c1365adf35c5d490d77500a4607fb_default.png"},{"username":"dizo89","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15658375/medium/0ca745e5017d491fe1b22b0239904de8.jpeg"},{"username":"tee_noodle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674577/medium/804d9cf06f5196026acb6436b809d0da_default.png"},{"username":"Marty88","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674593/medium/492b1509d52bd2809dea768121217125.jpeg"},{"username":"Spillebulle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15677023/medium/20b98bb85062e07afe0e63618f8a5feb.PNG"},{"username":"petitmewen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15685239/medium/15de9b62d2e0bc25013435f1784bbcc1.jpeg"},{"username":"NoProsNoNoobs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15687709/medium/ae8f65fafeb8bcf74dcb8871bbe46461.png"},{"username":"y.gybson","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15690777/medium/b5cb5d2d5768904ff6586c727e3a6c77.jpeg"},{"username":"inside90","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15704947/medium/c1355fcb30dd76f8e39d98d1d49f1c52.png"},{"username":"RJSkudra","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15709853/medium/c3abf2774913dc4e81fb261d36d7668c.png"},{"username":"binge203","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15713937/medium/77c1cfa4314673db80e6881fd9f64668.gif"},{"username":"Mailootje","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15714337/medium/743c3bc4ab1989966a375eeeec83d8b8.jpeg"},{"username":"tim-wiegers","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15719805/medium/08a96f064813350661cd6b20bf3d7d99.png"},{"username":"GkhnG","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15722911/medium/71a027caec489ef6ce82bcf1888329d0_default.png"},{"username":"Zoen-Millo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15760967/medium/9e956f11adc5b34f5636268b5c485dbf.jpg"},{"username":"HooinKyoma","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15792897/medium/9489f0a9b368e0e827ae758b740a2eed.jpeg"},{"username":"wathergeen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15794495/medium/a83b7fc80d180669ebfd6deb3544cfd0.png"},{"username":"MoeToo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15818233/medium/d35cd6953717706eaf20f6c143c62947.png"},{"username":"itodouble","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15836233/medium/e984caea18fb0673bf319fcf28cef649.png"},{"username":"loslocitos","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15847901/medium/fe30d12fd2cf38212f929e13b169f9ec.jpeg"},{"username":"Oversleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15849065/medium/06141f13a6d541d753f3c2f2947b8068_default.png"},{"username":"Beardy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15865139/medium/fca6b9d2b3f52e286d1568f52b83b6a0_default.png"},{"username":"raelyan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15875457/medium/2f4fda1d1aaa5dcc79b328baf3f03151.jpeg"},{"username":"dolphin738","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15913763/medium/ee6fede7b8528ca642329ada80d1cc18.png"},{"username":"brunotco","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15916719/medium/09db45880fc05abc18adb8d932a5ecf9_default.png"},{"username":"LukeIsHereToDevelop","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15924739/medium/3e6cd3940297bb4ba7bb039c93e6f918.png"},{"username":"kennit","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15925879/medium/6b0733ad3c5949b91c55e4d8b03db8a5_default.png"},{"username":"kuunpire","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15950309/medium/8192a4f08f07086828ac9f74ed29a169.jpeg"},{"username":"Sandor-dev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15951759/medium/0216c2da4eb028164ebbecf1c72f6271_default.png"},{"username":"Meierschlumpf","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15953187/medium/23c744faef1ab84fbdc9351a7850aab6.jpeg"},{"username":"harmlesscat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15970733/medium/dd8d0214a0250c932bb518b1b55e45a2_default.png"},{"username":"OrzWTF","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15976121/medium/4c4557cbff7ff7b0503455bc59c020e0.jpeg"},{"username":"tagaishi","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15977271/medium/eade504c83a5a1ff831c80a538fbdb44_default.png"},{"username":"azurite928","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15981895/medium/547ccc459ee123e78b5401c499f1022d.png"},{"username":"Ronner231","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16021342/medium/7734d550df2de5a2fec2ffff33e7024c.jpeg"},{"username":"ugyes","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16034148/medium/ed001e3f470a2dea9a8ce955b18e7bd5.png"},{"username":"rpieja","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16045554/medium/bd55baca2ef8b92502a760cc9ee7c505_default.png"},{"username":"flambyisyou","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16051620/medium/3a3bc0c90f6b95ab4ef74396a0a17beb.png"},{"username":"Topbcy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16077170/medium/d3aed33ea56330338756cfcd89477cfe.jpeg"},{"username":"ai5d02sb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16084674/medium/7c8119fe2a5ca71bb15f636916a42b95_default.png"},{"username":"explosiveparrot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16097722/medium/7762f80fc1da63f5b2eb87de9d640324_default.png"},{"username":"DestroyCom","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16111544/medium/83e3d3ac5c8bfed634cd1a074854db67.png"},{"username":"neotraxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16116966/medium/b30df957873329019a8a90a69f6efe3a.png"},{"username":"gzxpa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16123314/medium/487b05577310a946b0b98bc72c11213b_default.png"},{"username":"TORIK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16124732/medium/de770890e6411860ac06e8cc2dc0abd0_default.png"},{"username":"sergio.pibot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16138114/medium/80db802b2fcd6bbf19ea4c7413367dca.jpeg"},{"username":"karam-ajaj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16139838/medium/7539ab958d3c85d5f67e7849483ea8f7.jpeg"},{"username":"SimonHenz97","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16151520/medium/9b94a85b536688e2d94538c384fd0a76.png"},{"username":"olsson82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16198988/medium/eeaab4cba210c537424d4204754b604e.png"},{"username":"bo3bdo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16222806/medium/8652f3beacb6098dde5a4cd0a29fe80d.jpeg"},{"username":"AngryPlayer04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245192/medium/342715274f4dc31fd4132cdae9126188.jpeg"},{"username":"ehatamm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245884/medium/979d5e5526fe545be9bf46db481f7182.jpeg"},{"username":"Balionelis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16246058/medium/986d62ba363594c517d966fe9178dcbb.png"},{"username":"kwargs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16248816/medium/19ce11f04bf1ae2637e0483399c675f9_default.png"},{"username":"lucicu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16273892/medium/3b09e8f3bead221ef98d6a45a16851dc_default.png"},{"username":"Michael5564445","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16357688/medium/9d9026a8bf3eaccb0417da240932ebf7.png"},{"username":"Netscream","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16359198/medium/694e0c8572dc8a47e6d63da31ec5b992.png"},{"username":"belicstefan04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16368142/medium/4742dcfd7bf7ad7e33e37ac44fba7e0f.png"},{"username":"Hamad2066","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16371478/medium/f37bbc7685b6757defdbd1494236af61.jpeg"},{"username":"3ct0pl4sm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16399616/medium/ce796d1c40163975624c0a483e56374f.png"},{"username":"sanchez.marcell626","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16433865/medium/3ddde1314764cc2205ffdcd1ae719a91.png"},{"username":"Minoctis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16490079/medium/91fd0849303a4eca033d660d0a7b6854.jpeg"},{"username":"homarr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16657003/medium/3afabfd3de949a54ff5d40ad04552aba.png"},{"username":"mwgg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16658379/medium/763dd09f5c238cc5324d4d5357985292.png"},{"username":"kiwinho","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16671855/medium/bfe3a2d497dc4748a04d02e4ae2139cb.jpeg"},{"username":"tjvg4m34r13","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16679135/medium/7a483b9b0abe0d925e54808e1cb38fa0.jpeg"},{"username":"Diegoem","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16705921/medium/29ce68ebc191fc3f65499511b12de98f_default.png"},{"username":"bouks69","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16746091/medium/b58b1fc08520de416198be9df5d093eb.jpeg"},{"username":"futhgar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16748067/medium/b8c526b097f9aa9d687250cac3df16dc.jpeg"},{"username":"GrimSQL","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16764343/medium/fff729a76d1f8fbcdaee9bb0c0f5f3fd.png"},{"username":"chnikel","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767047/medium/3dda2a37e03049dafefe6697bb00f75c.png"},{"username":"yuki19870616","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767951/medium/f59c848e38382cda6c8464b0a7b9e642_default.png"},{"username":"noamdal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16782245/medium/b4a72a61f9caf138f0322c0cb987e6d0.png"},{"username":"MalithRukshan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16809057/medium/084d6a5b35fbbeacc70e7021197d222f.png"},{"username":"minirope19","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16930929/medium/e8cbbc26747e31c5a8959215e1dd067b.jpeg"},{"username":"prubin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17503492/medium/96ac87644691e49c1f983f2f094e541c_default.png"},{"username":"sam-tam-sam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17578052/medium/1628dc2954ce3ae95e0caaa2bd60ab0c.png"},{"username":"toineenzo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12371852/medium/9f1b2bedb4794c59479cd289bdba9fab.png"},{"username":"ArcaneWater","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12421105/medium/1ac82517a6c658f6b62c48aebcb17e86_default.png"},{"username":"Ebedami","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12974801/medium/b596dea5c09454c4c828eac558389755.jpg"},{"username":"perrygamerptyt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13117177/medium/1406c949507ecd438480473c8a1f6124.jpg"},{"username":"SergiSvK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13320543/medium/3df3c79cc5f0c53716c559cd3fd2fc20.png"},{"username":"jeongeon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13362856/medium/eb572f72cde5e5526c517ca3a6489850_default.png"},{"username":"MrTranCFCVN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13439475/medium/a1789d69c85d90c667194b5d6a518c08_default.png"},{"username":"Tobost06","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13467449/medium/9227dc07f6366bc060d28304f5cb8ce7.jpeg"},{"username":"Kf637","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13530622/medium/118b00bb72d01d3229c97409637e25d1_default.png"},{"username":"Cruellest","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13610615/medium/2a74faa0947ee08d18f7946fb8602c1e.jpg"},{"username":"olavinto","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14424644/medium/37c01732bd2b7a272b421807a05ccfeb.png"},{"username":"Thario","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14491372/medium/dd5e142070802a672900121aa3f332a6_default.png"},{"username":"Wjouter","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14585620/medium/b91a51513332bdc0a0a9ecbed99ecbc3.png"},{"username":"albanobattistella","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14686194/medium/564afcfd76f5cd24751bd669cf4943aa.jpg"},{"username":"Kachelkaiser","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14763406/medium/203a781797da4a18fe0f4e7db8fd547c.png"},{"username":"regi4","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14842936/medium/f7e1d672f562a7db61ba6b4b8f4de41d.png"},{"username":"baton5129","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14891064/medium/8d438f557b82977dd02f0ead3187265f.PNG"},{"username":"meviper","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15958903/medium/209f521ce33a05831fa8d7d1b7babc20_default.png"},{"username":"GerOriki","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16268376/medium/da00e54b08cd40348231d92873a0cd0a_default.png"},{"username":"TheRedCyclops","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16486399/medium/7f39226180120d5a9efcb8e13d8aec8e_default.png"},{"username":"dymek37","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16659221/medium/d1dc6d11d7e7fc370efc5070e837ae03.png"},{"username":"B3CKDOOR","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16744975/medium/e6c7b2d45e76465d54e6efb67c83da7d.png"},{"username":"Larsluph","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16779933/medium/ed7d36f8b44013bad3a16ac0491bd0f7.png"},{"username":"FOEDI","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16781001/medium/6b4552fb72e359aa016f7a054078fac8.png"},{"username":"xmarkurion","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16805443/medium/34a20428427de5aaef99c7d140646803.png"},{"username":"gregid","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16806705/medium/f3c8f4a20436e8538929130142bbc104.png"},{"username":"Flameberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16820453/medium/d47be866148474c9d05fec6fbe406d88.png"},{"username":"Playfust","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16872865/medium/5f4609cc711e5fb9fa8a0b178635769c.jpeg"},{"username":"Cotignac","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16879297/medium/81ff353edf030699a159bb79cda26e84.jpg"},{"username":"LeaHun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16916375/medium/618520f0543b1e212ebc83d66b683d4b_default.png"},{"username":"jackwellerreal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16919717/medium/37bbfdc03893f141133ccc619593266d.png"},{"username":"renan.costaalencar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16938427/medium/6d25b25320c5bde734118dec4af9cd5b.png"},{"username":"andrius.cim","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16967845/medium/46028fc1a0ddfaaeebcb37b39739625d_default.png"},{"username":"xjxexrxrxyx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17041412/medium/d34d664166db6fb25579bca9ff3b50b4.jpeg"},{"username":"bilhal.fellah","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17044572/medium/d1ec6f17e4aa013e9eededc83778d2c6.png"},{"username":"the_octo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17058060/medium/7423ab3a4cabb67f5decb2e2dfa2b675_default.png"},{"username":"BamesJond","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17075846/medium/128d7ffd40254c8e376f45678e2fc620_default.png"},{"username":"Kornikus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17134240/medium/d7a42b2c30db46ff152699ca01f9e11a_default.png"},{"username":"xoxyuxu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17176526/medium/108055aa24343043de852e412bec74be.png"},{"username":"cryptodntsleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17249408/medium/b9e15aa2b24a0dbc1e4f194aa8ed5109.png"},{"username":"yaniorg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17261842/medium/7dbfc77f21c826845961243c44540d26.jpg"},{"username":"Vidariondr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17262438/medium/45fbc71be02ef27296b6f423e97b6a03_default.png"},{"username":"Fastery","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17263066/medium/701b0edc59676174863ad651528bbb37.jpeg"},{"username":"stratself","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17290388/medium/57d9d8161b3fd0abf4dc5558b4a1fa05_default.png"},{"username":"wuzeqin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17292606/medium/b9e0308410b31337e2d7aa6ed346d718_default.png"},{"username":"ChristopherBull","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17304338/medium/21a175ce596bfae81430767467908b90.jpeg"},{"username":"darkbcnlord","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17311436/medium/b271bc0b419ec3a26ddb7b63ff8ffc4b.png"},{"username":"zhoran1997","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17344028/medium/c8f450b5a8f1e9494a45dc60af7ce63e.png"},{"username":"gilalves1703","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17447768/medium/3f1de562a5c2391c88bbf8d5cf40a266.png"},{"username":"iServeCloud","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17470960/medium/11649b99d609438d66455a4d2fdc70f7.png"},{"username":"guibolzantvbox","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17474136/medium/59a576e6da538664ceb972253916fa02.jpeg"},{"username":"ivann136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17506062/medium/67d09ada36fac5f444a1f7517e3a97bd_default.png"},{"username":"Maheshivara","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17516082/medium/576e1e6581cf969615b265de6aeff8b7.jpeg"},{"username":"brunogui81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17517542/medium/7c0d174e810435af518fe8e0c05e151f.jpeg"},{"username":"michelwilhelm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17542190/medium/4c6b33fd8993ff565fa75add2ce8355e.jpeg"},{"username":"dias-gxstavo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17547376/medium/8c6b109aae8aeb651192346a39ae9e5b.jpeg"},{"username":"ToxykAuBleu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17614030/medium/8d21e0608560172b121bd46126d827c7.jpeg"},{"username":"e.erdemir","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17617994/medium/7259a29db9941c6171a8cbbd643b92bd.png"},{"username":"sjonkreutz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17618306/medium/7601c45d015189a863656e21348d223b.png"},{"username":"volovich0377","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17620388/medium/c929a8fbd24f96d17620fa497ade7309.png"},{"username":"Hamstio1","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17624898/medium/406e0bf40094c24a01c3064f8f5b2a82.png"},{"username":"i3laa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17627462/medium/613a9d283ed8b69819ee181903851660.jpeg"},{"username":"bastiengrignon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17629884/medium/ddfc3339285bd65195fcd834e512e9b7.jpeg"},{"username":"kauhee","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17644600/medium/def1b9066cf9275b4273a5a5dca7f325.png"},{"username":"hongminzheng","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17683860/medium/a764f4963912777d27a7cd98e136a2b2.png"}]
\ No newline at end of file
diff --git a/tooling/github/setup/action.yml b/tooling/github/setup/action.yml
index 519e1a7ac..b8374d497 100644
--- a/tooling/github/setup/action.yml
+++ b/tooling/github/setup/action.yml
@@ -10,8 +10,5 @@ runs:
node-version: 24.15.0
cache: "pnpm"
- - shell: bash
- run: pnpm add -g turbo
-
- shell: bash
run: pnpm install --frozen-lockfile