Compare commits

...
38 Commits
Author SHA1 Message Date
homarr-renovate[bot] dbda477c77 chore(deps): update dependency next-auth to v5.0.0-beta.32 [security] (#6840)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-09-13 14:25:25 +00:00
homarr-renovate[bot] a5c92b114e chore(deps): update dependency hono@<4.6.5 to >=4.13.7 [security] (#6842)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-09-13 03:53:52 +00:00
homarr-renovate[bot] 58ac28d6b5 chore(deps): update dependency @auth/core to v0.41.3 [security] (#6839)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-09-13 00:05:05 +00:00
Kushie 43a57a2dad fix(firewall widget): allow content to shrink and scroll (#6818) 2026-09-12 10:49:01 +02:00
Releases Homarr 7859faaa1d chore(release): 1.77.1 [skip ci] 2026-09-11 19:22:36 +00:00
homarr-releases[bot] bff6ec735e chore(release): automatic release v1.77.1 2026-09-11 19:21:19 +00:00
Thomas 1fbe844598 fix(settings): move board access control out of the nested form (#6662) 2026-09-10 18:13:56 +02:00
Thomas 365ef7f02b Revert "feat: announce Homarr v2 beta" (#6813) 2026-09-10 16:36:54 +02:00
homarr-releases[bot] 2b1a03849d chore: update bug report template (#6798) 2026-09-06 22:31:26 +02:00
Releases Homarr 4c243bc374 chore(release): 1.77.0 [skip ci] 2026-09-06 19:31:37 +00:00
homarr-releases[bot] ecd02f73c0 chore(release): automatic release v1.77.0 2026-09-06 19:30:29 +00:00
Thomas cd23b36f3c fix(ci): use pinned pnpm for releases (#6796) 2026-09-06 21:18:30 +02:00
homarr-releases[bot] f7f29eade9 chore(release): automatic release v1.77.0 2026-09-06 18:47:01 +00:00
Thomas 32a51c1e60 feat: announce Homarr v2 beta (#6793)
* feat: announce Homarr v2 beta

* chore: refine v2 beta announcement

* feat: expand v2 beta guidance

* chore: refine v2 beta resources

* chore: simplify v2 beta modal

* chore: improve v2 beta feedback links

* fix: keep v2 modal above header

* chore: sharpen v2 beta message

* fix: clear header for v2 modal
2026-09-06 20:36:04 +02:00
homarr-crowdin[bot] da32acb4f0 chore(lang): updated translations from crowdin
* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-09-06 01:52:04 +00:00
homarr-update-contributors[bot] 1da13d9bb3 chore: update contributors (#6767)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-09-04 15:58:09 +00:00
homarr-crowdin[bot] cb0fec088f chore(lang): updated translations from crowdin
* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-09-03 02:05:21 +00:00
homarr-crowdin[bot] a2fe8dd461 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-09-01 02:36:36 +00:00
homarr-update-contributors[bot] b6dfcd69ba chore: update contributors (#6735)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-08-28 21:54:00 +00:00
homarr-crowdin[bot]andThomas a38b0ef71d chore(lang): updated translations from crowdin (#6732)
Co-authored-by: Thomas <thomas@ajnart.fr>
2026-08-28 14:07:42 +02:00
Thomas 2cf3027eeb Merge pull request #6720 from homarr-labs/docs/update-helm-docs
docs(helm): update chart installation page
2026-08-28 08:32:40 +02:00
homarr-releases[bot] fa18f6fc71 chore: update bug report template (#6724) 2026-08-28 08:32:03 +02:00
Releases Homarr 331925c197 chore(release): 1.76.2 [skip ci] 2026-08-27 20:09:22 +00:00
Thomas 0e51f745d3 Merge pull request #6723 from homarr-labs/dev
chore(release): automatic release v1.76.2
2026-08-27 22:08:05 +02:00
Thomas d6f720d0e6 Revert "feat(http): set custom User-Agent on outbound integration requests (#6606)" (#6722)
This reverts commit ddbf05524b.
2026-08-27 22:00:23 +02:00
Thomas 5324bf12d4 fix(startup): use stable internal service addresses (#6721) 2026-08-27 21:52:54 +02:00
github-actions[bot] 2922a02ecf docs(helm): update chart installation page 2026-08-27 17:09:27 +00:00
Thomas 87cdcf28e0 fix(mcp): return valid string for void tool results (#6703) 2026-08-27 11:10:37 +02:00
homarr-releases[bot] 441b181e79 chore: update bug report template (#6716)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-08-27 11:08:43 +02:00
Releases Homarr eba639b59c chore(release): 1.76.1 [skip ci] 2026-08-26 16:43:52 +00:00
Thomas Camlong da36283ad1 chore(release): retrigger release pipeline 2026-08-26 18:30:12 +02:00
Thomas Camlong c050368782 chore(release): trigger release pipeline 2026-08-26 18:16:26 +02:00
homarr-releases[bot] 82b50f0fd7 chore(release): automatic release v1.76.1 2026-08-26 15:50:55 +00:00
Thomas a0149d5894 Merge pull request #6673 from homarr-labs/investigate-6672-immich-401 2026-08-22 19:44:03 +02:00
ajnart 8579fcbfc2 test(http,immich): address CodeRabbit review
- guarantee server teardown via try/finally in both wire-level tests
- add tuple-array HeadersInit coverage to the helper suite
2026-08-22 17:21:38 +02:00
ajnart b6f3aba23e test(http,immich): prove User-Agent fix at the wire level
Add two regression tests that fail on v1.76.0 and pass with the fix:
- wire-level test: a Headers instance (the shape @immich/sdk hands over)
  must reach the remote server with all headers intact
- real-SDK e2e: getServerStatistics through fetchWithTrustedCertificatesAsync
  against a live local server must deliver x-api-key
2026-08-22 17:16:31 +02:00
ajnart 31e218295d fix(http): preserve SDK headers when injecting User-Agent
The v1.76.0 User-Agent injection object-spread `fetchOptions.headers`, which
silently drops all headers when the value is a Headers instance. The Immich
SDK passes a Headers object with the merged x-api-key header, so requests
lost their API key and returned 401 (issue #6672). Build a real Headers
instance instead, keeping case-insensitive user-agent handling.
2026-08-22 17:12:43 +02:00
Thomas aca3b5d861 fix: correct Unraid CPU, link context menus, and board resizing (#6510)
Fix Unraid logical CPU averaging, preserve native context menus for app and bookmark widgets in view mode, and prevent scrollbar-width GridStack resize oscillation.
2026-08-22 10:53:26 +02:00
31 changed files with 679 additions and 555 deletions

No files matched your search

+3
View File
@@ -33,6 +33,9 @@ body:
options: options:
# The below comment is used to insert a new version with on-release.yml # The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION# #NEXT_VERSION#
- 1.77.0
- 1.76.2
- 1.76.1
- 1.76.0 - 1.76.0
- 1.75.0 - 1.75.0
- 1.74.0 - 1.74.0
@@ -68,8 +68,6 @@ jobs:
with: with:
node-version: 24.18.0 node-version: 24.18.0
package-manager-cache: false package-manager-cache: false
- run: npm i -g pnpm
if: env.SKIP_RELEASE == 'false'
- name: Install dependencies - name: Install dependencies
if: env.SKIP_RELEASE == 'false' if: env.SKIP_RELEASE == 'false'
run: | run: |
+8
View File
@@ -1,3 +1,11 @@
## [1.77.1](https://github.com/homarr-labs/homarr/compare/v1.77.0...v1.77.1) (2026-09-11)
## [1.77.0](https://github.com/homarr-labs/homarr/compare/v1.76.2...v1.77.0) (2026-09-06)
## [1.76.2](https://github.com/homarr-labs/homarr/compare/v1.76.1...v1.76.2) (2026-08-27)
## [1.76.1](https://github.com/homarr-labs/homarr/compare/v1.76.0...v1.76.1) (2026-08-26)
## [1.76.0](https://github.com/homarr-labs/homarr/compare/v1.75.0...v1.76.0) (2026-08-21) ## [1.76.0](https://github.com/homarr-labs/homarr/compare/v1.75.0...v1.76.0) (2026-08-21)
## [1.75.0](https://github.com/homarr-labs/homarr/compare/v1.74.0...v1.75.0) (2026-08-14) ## [1.75.0](https://github.com/homarr-labs/homarr/compare/v1.74.0...v1.75.0) (2026-08-14)
@@ -2,9 +2,9 @@
<img src="https://raw.githubusercontent.com/homarr-labs/charts/refs/heads/main/charts/homarr/icon.svg" align="right" width="92" alt="homarr logo"> <img src="https://raw.githubusercontent.com/homarr-labs/charts/refs/heads/main/charts/homarr/icon.svg" align="right" width="92" alt="homarr logo">
![Version: 8.28.0](https://img.shields.io/badge/Version-8.28.0-informational?style=flat) ![Version: 8.28.1](https://img.shields.io/badge/Version-8.28.1-informational?style=flat)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat)
![AppVersion: v1.76.0](https://img.shields.io/badge/AppVersion-v1.76.0-informational?style=flat) ![AppVersion: v1.76.1](https://img.shields.io/badge/AppVersion-v1.76.1-informational?style=flat)
A Helm chart to deploy homarr for Kubernetes A Helm chart to deploy homarr for Kubernetes
@@ -429,7 +429,7 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match | | httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy | | image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository | | image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
| image.tag | string | `"v1.76.0"` | Overrides the image tag whose default is the chart appVersion | | image.tag | string | `"v1.76.1"` | Overrides the image tag whose default is the chart appVersion |
| imagePullSecrets | list | `[]` | Secrets for Docker registry | | imagePullSecrets | list | `[]` | Secrets for Docker registry |
| ingress.annotations | object | `{}` | Ingress annotations | | ingress.annotations | object | `{}` | Ingress annotations |
| ingress.enabled | bool | `false` | Enable ingress | | ingress.enabled | bool | `false` | Enable ingress |
+13 -15
View File
@@ -1,30 +1,27 @@
--- ---
title: 'App' title: "App"
description: 'Embeds an app into the board.' description: "Embeds an app into the board."
hide_title: true hide_title: true
--- ---
import { AddingWidget } from '@site/src/components/widgets/adding'; import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from '@site/src/components/widgets/configuration'; import { WidgetConfig } from "@site/src/components/widgets/configuration";
import { WidgetHeader } from '@site/src/components/widgets/header'; import { WidgetHeader } from "@site/src/components/widgets/header";
import { appWidget } from '.'; import { appWidget } from ".";
<WidgetHeader widget={appWidget} categories={["Links", "Apps"]} />
<WidgetHeader
widget={appWidget}
categories={['Links', 'Apps']}
/>
The app widget allows you to add a link to an application or a website that you want to access quickly from your Homarr board. The app widget allows you to add a link to an application or a website that you want to access quickly from your Homarr board.
It also supports a status check to indicate whether Homarr can reach the application or website. It also supports a status check to indicate whether Homarr can reach the application or website.
In view mode, right-clicking uses the browser's link menu. Homarr's widget menu remains available in edit mode.
### Screenshots ### Screenshots
import githubApp from './img/github.png'; import githubApp from "./img/github.png";
import jellyseerrApp from './img/jellyseerr-description-tooltip.png'; import jellyseerrApp from "./img/jellyseerr-description-tooltip.png";
import piHoleApp from './img/pi-hole-ping-icon.png'; import piHoleApp from "./img/pi-hole-ping-icon.png";
<div className='flex gap-1'> <div className="flex gap-1">
<img src={githubApp} alt="app github with-status-check" width={128 * 1.5} height={128 * 1.5} /> <img src={githubApp} alt="app github with-status-check" width={128 * 1.5} height={128 * 1.5} />
<img src={piHoleApp} alt="app pi-hole with-status-check status-check-icon" width={128 * 1.5} height={128 * 1.5} /> <img src={piHoleApp} alt="app pi-hole with-status-check status-check-icon" width={128 * 1.5} height={128 * 1.5} />
<img src={jellyseerrApp} alt="app jellyseerr with-description-tooltip" height={128 * 1.5} /> <img src={jellyseerrApp} alt="app jellyseerr with-description-tooltip" height={128 * 1.5} />
@@ -34,4 +31,5 @@ import piHoleApp from './img/pi-hole-ping-icon.png';
<AddingWidget /> <AddingWidget />
### Configuration ### Configuration
<WidgetConfig configuration={appWidget.configuration} /> <WidgetConfig configuration={appWidget.configuration} />
+14 -16
View File
@@ -1,29 +1,26 @@
--- ---
title: 'Bookmarks' title: "Bookmarks"
description: 'Displays multiple app links' description: "Displays multiple app links"
hide_title: true hide_title: true
--- ---
import { AddingWidget } from '@site/src/components/widgets/adding'; import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from '@site/src/components/widgets/configuration'; import { WidgetConfig } from "@site/src/components/widgets/configuration";
import { WidgetHeader } from '@site/src/components/widgets/header'; import { WidgetHeader } from "@site/src/components/widgets/header";
import { bookmarksWidget } from '.'; import { bookmarksWidget } from ".";
<WidgetHeader widget={bookmarksWidget} categories={["Links", "Apps"]} />
<WidgetHeader
widget={bookmarksWidget}
categories={['Links', 'Apps']}
/>
The bookmarks widget allows you to display multiple app links in a single widget. The bookmarks widget allows you to display multiple app links in a single widget.
You can add links to your favorite applications, websites, or any other resources you want quick access to. You can add links to your favorite applications, websites, or any other resources you want quick access to.
In view mode, right-clicking uses the browser's link menu. Homarr's widget menu remains available in edit mode.
### Screenshots ### Screenshots
import gridIconsOnly from './img/grid-icons-only.png'; import gridIconsOnly from "./img/grid-icons-only.png";
import gridWithTitle from './img/grid-with-title.png'; import gridWithTitle from "./img/grid-with-title.png";
import verticalListAll from './img/vertical-list-all.png'; import verticalListAll from "./img/vertical-list-all.png";
import vericalListSimple from './img/vertical-list-simple.png'; import vericalListSimple from "./img/vertical-list-simple.png";
<div className='flex gap-1'> <div className='flex gap-1'>
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
@@ -33,12 +30,13 @@ import vericalListSimple from './img/vertical-list-simple.png';
<img src={gridWithTitle} alt="bookmarks grid with-title" width={256 + 4} height={256 + 4} /> <img src={gridWithTitle} alt="bookmarks grid with-title" width={256 + 4} height={256 + 4} />
<img src={verticalListAll} alt="bookmarks vertical-list all-elements" width={128} height={256 + 4} /> <img src={verticalListAll} alt="bookmarks vertical-list all-elements" width={128} height={256 + 4} />
</div>
</div>
### Adding the widget ### Adding the widget
<AddingWidget /> <AddingWidget />
### Configuration ### Configuration
<WidgetConfig configuration={bookmarksWidget.configuration} /> <WidgetConfig configuration={bookmarksWidget.configuration} />
@@ -178,35 +178,37 @@ export const BoardSettingsForm = ({ board, permissions, hasFullAccess, hideVisib
const isPending = savePartialSettings.isPending || saveLayouts.isPending; const isPending = savePartialSettings.isPending || saveLayouts.isPending;
return ( return (
<form onSubmit={form.onSubmit((values) => void handleSubmitAsync(values))}> <Stack gap="xl">
<Stack gap="xl"> <form onSubmit={form.onSubmit((values) => void handleSubmitAsync(values))}>
<GeneralSettingsContent board={board} form={form} /> <Stack gap="xl">
<LayoutSettingsContent form={form} /> <GeneralSettingsContent board={board} form={form} />
<BackgroundSettingsContent form={form} /> <LayoutSettingsContent form={form} />
<ColorSettingsContent form={form} /> <BackgroundSettingsContent form={form} />
<CustomCssSettingsContent form={form} /> <ColorSettingsContent form={form} />
<BehaviorSettingsContent form={form} /> <CustomCssSettingsContent form={form} />
<BehaviorSettingsContent form={form} />
{hasFullAccess && ( {form.isDirty() && (
<SectionCard title={tSection("access.title")}> <UnsavedChangesBar>
<BoardAccessSettings board={board} initialPermissions={permissions} /> <Button type="button" disabled={isPending} variant="default" onClick={handleDiscard}>
</SectionCard> {t("common.action.discard")}
)} </Button>
<Button loading={isPending} type="submit" disabled={!form.isValid()}>
{t("common.action.saveChanges")}
</Button>
</UnsavedChangesBar>
)}
</Stack>
</form>
{hasFullAccess && <DangerZoneSettingsContent hideVisibility={hideVisibility} />} {hasFullAccess && (
<SectionCard title={tSection("access.title")}>
<BoardAccessSettings board={board} initialPermissions={permissions} />
</SectionCard>
)}
{form.isDirty() && ( {hasFullAccess && <DangerZoneSettingsContent hideVisibility={hideVisibility} />}
<UnsavedChangesBar> </Stack>
<Button type="button" disabled={isPending} variant="default" onClick={handleDiscard}>
{t("common.action.discard")}
</Button>
<Button loading={isPending} type="submit" disabled={!form.isValid()}>
{t("common.action.saveChanges")}
</Button>
</UnsavedChangesBar>
)}
</Stack>
</form>
); );
}; };
@@ -14,6 +14,7 @@ import { db } from "@homarr/db";
import { getPackageVersion } from "~/versions/package-reader"; import { getPackageVersion } from "~/versions/package-reader";
import { extractMcpTools } from "../_extract-tools"; import { extractMcpTools } from "../_extract-tools";
import { serializeMcpToolResult } from "../_serialize-result";
const logger = createLogger({ module: "mcpRoute" }); const logger = createLogger({ module: "mcpRoute" });
@@ -203,7 +204,7 @@ const mcpHandler = createMcpHandler(
const input = args && Object.keys(args).length > 0 ? args : undefined; const input = args && Object.keys(args).length > 0 ? args : undefined;
const result = await procedure(input); const result = await procedure(input);
return { return {
content: [{ type: "text" as const, text: JSON.stringify(result) }], content: [{ type: "text" as const, text: serializeMcpToolResult(result) }],
}; };
} catch (error) { } catch (error) {
const message = sanitizeErrorMessage(error); const message = sanitizeErrorMessage(error);
@@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { serializeMcpToolResult } from "./_serialize-result";
describe("serializeMcpToolResult", () => {
it("serializes objects as JSON strings", () => {
expect(serializeMcpToolResult({ boardId: "1" })).toBe('{"boardId":"1"}');
});
it("serializes primitive values", () => {
expect(serializeMcpToolResult("hello")).toBe('"hello"');
expect(serializeMcpToolResult(42)).toBe("42");
expect(serializeMcpToolResult(true)).toBe("true");
});
it("serializes null as the string null", () => {
expect(serializeMcpToolResult(null)).toBe("null");
});
it("serializes void results as the string null so MCP content text is always a string", () => {
expect(serializeMcpToolResult(undefined)).toBe("null");
});
});
@@ -0,0 +1,3 @@
export function serializeMcpToolResult(result: unknown): string {
return JSON.stringify(result) ?? "null";
}
@@ -44,9 +44,13 @@ interface WidgetContextMenuProps {
export const WidgetContextMenu = ({ item, definition, widgetStateRef, children }: WidgetContextMenuProps) => { export const WidgetContextMenu = ({ item, definition, widgetStateRef, children }: WidgetContextMenuProps) => {
const { data: session } = useSession(); const { data: session } = useSession();
const [isEditMode] = useEditMode();
const settings = useSettings(); const settings = useSettings();
if (!session || !settings.enableRightClickOnWidgets) return <>{children}</>; if (!session || !settings.enableRightClickOnWidgets) return <>{children}</>;
const isLinkWidget = item.kind === "app" || item.kind === "bookmarks";
// Keep the Homarr menu in edit mode so these widgets can still be edited or removed.
if (isLinkWidget && !isEditMode) return <>{children}</>;
return ( return (
<WidgetContextMenuInner item={item} definition={definition} widgetStateRef={widgetStateRef} settings={settings}> <WidgetContextMenuInner item={item} definition={definition} widgetStateRef={widgetStateRef} settings={settings}>
@@ -301,6 +301,8 @@ interface UseCssVariableConfiguration {
isDynamic: boolean; isDynamic: boolean;
} }
const minimumResizeWidthDelta = 20;
/** /**
* This hook is used to configure the css variables for the gridstack * This hook is used to configure the css variables for the gridstack
* Those css variables are used to define the size of the gridstack items * Those css variables are used to define the size of the gridstack items
@@ -319,9 +321,30 @@ const useCssVariableConfiguration = ({
columnCount, columnCount,
isDynamic, isDynamic,
}: UseCssVariableConfiguration) => { }: UseCssVariableConfiguration) => {
const prevSizeRef = useRef<{ width: number; height: number } | null>(null);
const onResize = useCallback(() => { const onResize = useCallback(() => {
if (!wrapperRef.current) return; if (!wrapperRef.current) return;
if (!gridRef.current) return; if (!gridRef.current) return;
const currentWidth = wrapperRef.current.clientWidth;
const currentHeight = wrapperRef.current.clientHeight;
const prevSize = prevSizeRef.current;
// Only skip recalculation when the width change is insignificant
// and, for dynamic sections, the height is unchanged. This prevents infinite
// feedback loops when the vertical scrollbar appears/disappears, causing
// clientWidth to oscillate by ~15px, while still reacting to height changes.
if (
prevSize !== null &&
Math.abs(currentWidth - prevSize.width) < minimumResizeWidthDelta &&
(!isDynamic || currentHeight === prevSize.height)
) {
return;
}
prevSizeRef.current = { width: currentWidth, height: currentHeight };
handleResizeChange( handleResizeChange(
wrapperRef.current, wrapperRef.current,
gridRef.current, gridRef.current,
+2 -2
View File
@@ -9,7 +9,7 @@ http {
# Route websockets traffic to port 3001 # Route websockets traffic to port 3001
location /websockets { location /websockets {
proxy_pass http://${HOSTNAME}:3001; proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade"; proxy_set_header Connection "Upgrade";
@@ -18,7 +18,7 @@ http {
# Route all other traffic to port 3000 # Route all other traffic to port 3000
location / { location / {
proxy_pass http://${HOSTNAME}:3000; proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "homarr", "name": "homarr",
"version": "1.76.0", "version": "1.77.1",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "cross-env CI=true turbo build", "build": "cross-env CI=true turbo build",
+1 -1
View File
@@ -43,7 +43,7 @@ async function importCookiesAsync() {
function getBaseUrl() { function getBaseUrl() {
if (typeof window !== "undefined") return window.location.origin; if (typeof window !== "undefined") return window.location.origin;
return `http://${process.env.HOSTNAME ?? "localhost"}:3000`; return "http://127.0.0.1:3000";
} }
export const trpcPath = "/api/trpc"; export const trpcPath = "/api/trpc";
@@ -13,11 +13,6 @@ import { UndiciHttpAgent } from "@homarr/core/infrastructure/http";
import type { TrustedCertificateHostname } from "../certificates/hostnames"; import type { TrustedCertificateHostname } from "../certificates/hostnames";
import { withTimeoutAsync } from "./timeout"; import { withTimeoutAsync } from "./timeout";
import packageJson from "../../../../../package.json";
export const getDefaultUserAgent = () => `Homarr/${packageJson.version} (+https://homarr.dev)`;
export const createCustomCheckServerIdentity = ( export const createCustomCheckServerIdentity = (
trustedHostnames: TrustedCertificateHostname[], trustedHostnames: TrustedCertificateHostname[],
): typeof checkServerIdentity => { ): typeof checkServerIdentity => {
@@ -83,10 +78,6 @@ export const fetchWithTrustedCertificatesAsync = async (
async (signal) => async (signal) =>
fetch(url, { fetch(url, {
...fetchOptions, ...fetchOptions,
headers: {
"User-Agent": getDefaultUserAgent(),
...fetchOptions.headers,
},
signal, signal,
dispatcher: agent, dispatcher: agent,
}), }),
@@ -97,10 +88,6 @@ export const fetchWithTrustedCertificatesAsync = async (
const { bodyTimeout: _bodyTimeout, dispatcher: _dispatcher, ...fetchOptions } = options ?? {}; const { bodyTimeout: _bodyTimeout, dispatcher: _dispatcher, ...fetchOptions } = options ?? {};
return fetch(url, { return fetch(url, {
...fetchOptions, ...fetchOptions,
headers: {
"User-Agent": getDefaultUserAgent(),
...fetchOptions.headers,
},
dispatcher: agent, dispatcher: agent,
}); });
}; };
@@ -37,9 +37,16 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit
const systemInfo = await this.getSystemInformationAsync(); const systemInfo = await this.getSystemInformationAsync();
const cpuUtilization = systemInfo.metrics.cpu.cpus.reduce((acc, val) => acc + val.percentTotal, 0); const cpuUtilization = systemInfo.metrics.cpu.cpus.reduce((acc, val) => acc + val.percentTotal, 0);
const cpuCount = systemInfo.info.cpu.cores; const cpuCount = systemInfo.metrics.cpu.cpus.length;
// Each entry in cpus[] is one logical CPU (thread). Dividing the summed per-thread
// utilization by the thread count (not physical cores) gives the correct average and
// avoids over-reporting on SMT systems. Guard against zero to avoid Infinity/NaN.
let cpuUtilizationNormalized = 0;
if (cpuCount > 0) {
cpuUtilizationNormalized = cpuUtilization / cpuCount;
}
// We use "info" object instead of the stats since this is the exact amount the kernel sees, which is what Unraid displays.
const totalMemory = systemInfo.info.memory.layout.reduce((acc, layout) => layout.size + acc, 0); const totalMemory = systemInfo.info.memory.layout.reduce((acc, layout) => layout.size + acc, 0);
const usedMemory = totalMemory * (systemInfo.metrics.memory.percentTotal / 100); const usedMemory = totalMemory * (systemInfo.metrics.memory.percentTotal / 100);
const uptime = dayjs(systemInfo.info.os.uptime); const uptime = dayjs(systemInfo.info.os.uptime);
@@ -47,7 +54,7 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit
return { return {
version: systemInfo.info.os.release, version: systemInfo.info.os.release,
cpuModelName: systemInfo.info.cpu.brand, cpuModelName: systemInfo.info.cpu.brand,
cpuUtilization: cpuUtilization / cpuCount, cpuUtilization: cpuUtilizationNormalized,
memUsedInBytes: usedMemory, memUsedInBytes: usedMemory,
memAvailableInBytes: totalMemory - usedMemory, memAvailableInBytes: totalMemory - usedMemory,
uptime: dayjs().diff(uptime, "seconds"), uptime: dayjs().diff(uptime, "seconds"),
@@ -139,7 +146,7 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit
}, },
memory { memory {
layout { layout {
size size
} }
} }
} }
@@ -0,0 +1,39 @@
// @vitest-environment node
import { createServer } from "node:http";
import type { AddressInfo } from "node:net";
import { getServerStatistics, init } from "@immich/sdk";
import { describe, expect, test, vi } from "vitest";
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
vi.mock("@homarr/core/infrastructure/certificates", () => ({
getAllTrustedCertificatesAsync: vi.fn(async () => []),
getTrustedCertificateHostnamesAsync: vi.fn(async () => []),
}));
describe("real @immich/sdk end-to-end through fetchWithTrustedCertificatesAsync", () => {
test("x-api-key set via init() reaches the server", async () => {
let receivedXApiKey: string | string[] | undefined;
const server = createServer((req, res) => {
receivedXApiKey = req.headers["x-api-key"];
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ usage: 1000, photos: 5, videos: 2 }));
});
await new Promise<void>((resolve) => server.listen(0, resolve));
const port = (server.address() as AddressInfo).port;
init({ baseUrl: `http://127.0.0.1:${port}/api`, apiKey: "sdk-configured-key" });
try {
const stats = await getServerStatistics({
fetch: fetchWithTrustedCertificatesAsync as unknown as typeof fetch,
});
expect(receivedXApiKey).toBe("sdk-configured-key");
expect(stats).toMatchObject({ usage: 1000, photos: 5, videos: 2 });
} finally {
server.close();
}
});
});
+10 -10
View File
@@ -3163,8 +3163,8 @@
"noColumns": "Spalten in Elementen auswählen", "noColumns": "Spalten in Elementen auswählen",
"noCommunications": "Kann keine Daten von der Integration laden", "noCommunications": "Kann keine Daten von der Integration laden",
"noItems": "", "noItems": "",
"actionFailed": "", "actionFailed": "Aktion fehlgeschlagen",
"actionFailedMessage": "" "actionFailedMessage": "Diese Aktion konnte nicht abgeschlossen werden. Bitte versuche es erneut."
}, },
"items": { "items": {
"actions": { "actions": {
@@ -3452,7 +3452,7 @@
"queued": "" "queued": ""
}, },
"type": { "type": {
"movie": "", "movie": "Film",
"episode": "" "episode": ""
}, },
"empty": { "empty": {
@@ -3812,7 +3812,7 @@
}, },
"resource": { "resource": {
"routers": "", "routers": "",
"services": "", "services": "Dienste",
"middlewares": "" "middlewares": ""
}, },
"status": { "status": {
@@ -4098,7 +4098,7 @@
"temp": "", "temp": "",
"battery": "", "battery": "",
"services": "Dienste", "services": "Dienste",
"uptime": "", "uptime": "Verfügbarkeit",
"agent": "Agent" "agent": "Agent"
} }
}, },
@@ -4173,12 +4173,12 @@
"chart": { "chart": {
"cpu": { "cpu": {
"title": "CPU Auslastung", "title": "CPU Auslastung",
"subtitle": "", "subtitle": "Durchschnittliche Systemweite CPU-Auslastung",
"series": "CPU" "series": "CPU"
}, },
"memory": { "memory": {
"title": "", "title": "Speicherverbrauch",
"subtitle": "", "subtitle": "Genaue Auslastung zum erfassten Zeitpunkt",
"series": "Belegt", "series": "Belegt",
"cache": "" "cache": ""
}, },
@@ -4189,7 +4189,7 @@
}, },
"diskIO": { "diskIO": {
"title": "Festplatten-E/A", "title": "Festplatten-E/A",
"subtitle": "", "subtitle": "Durchsatz des Root-Dateisystems",
"read": "Lesen", "read": "Lesen",
"write": "Schreiben" "write": "Schreiben"
}, },
@@ -6096,7 +6096,7 @@
} }
}, },
"availability": { "availability": {
"available": "", "available": "Verfügbar",
"partiallyAvailable": "", "partiallyAvailable": "",
"processing": "", "processing": "",
"requested": "", "requested": "",
+21 -21
View File
@@ -3179,7 +3179,7 @@
"detailsTitle": "" "detailsTitle": ""
}, },
"downSpeed": { "downSpeed": {
"columnTitle": "", "columnTitle": "Down",
"detailsTitle": "" "detailsTitle": ""
}, },
"index": { "index": {
@@ -3194,10 +3194,10 @@
"columnTitle": "" "columnTitle": ""
}, },
"name": { "name": {
"columnTitle": "" "columnTitle": "Job name"
}, },
"progress": { "progress": {
"columnTitle": "", "columnTitle": "Progress",
"detailsTitle": "" "detailsTitle": ""
}, },
"ratio": { "ratio": {
@@ -3217,7 +3217,7 @@
"detailsTitle": "" "detailsTitle": ""
}, },
"state": { "state": {
"columnTitle": "", "columnTitle": "State",
"detailsTitle": "" "detailsTitle": ""
}, },
"time": { "time": {
@@ -3315,7 +3315,7 @@
} }
}, },
"mediaRequests-requestList": { "mediaRequests-requestList": {
"name": "", "name": "Media Requests List",
"description": "", "description": "",
"option": { "option": {
"linksTargetNewTab": { "linksTargetNewTab": {
@@ -3337,15 +3337,15 @@
"unknown": "", "unknown": "",
"pending": "", "pending": "",
"processing": "", "processing": "",
"requested": "", "requested": "Requested",
"partiallyAvailable": "", "partiallyAvailable": "",
"available": "", "available": "",
"blacklisted": "", "blacklisted": "Blacklisted",
"deleted": "" "deleted": ""
}, },
"status": { "status": {
"pending": "", "pending": "",
"approved": "", "approved": "Approved",
"declined": "", "declined": "",
"failed": "", "failed": "",
"completed": "" "completed": ""
@@ -3358,7 +3358,7 @@
"option": {}, "option": {},
"titles": { "titles": {
"stats": { "stats": {
"main": "", "main": "Media Stats",
"approved": "", "approved": "",
"pending": "", "pending": "",
"processing": "", "processing": "",
@@ -3402,15 +3402,15 @@
"panel": { "panel": {
"statistics": { "statistics": {
"empty": "", "empty": "",
"transcodes": "", "transcodes": "Transcodes",
"transcodesCount": "", "transcodesCount": "",
"healthChecksCount": "", "healthChecksCount": "",
"filesCount": "", "filesCount": "",
"savedSpace": "", "savedSpace": "",
"healthChecks": "", "healthChecks": "Health checks",
"videoCodecs": "", "videoCodecs": "Codecs",
"videoContainers": "", "videoContainers": "Containers",
"videoResolutions": "" "videoResolutions": "Resolutions"
}, },
"workers": { "workers": {
"empty": "", "empty": "",
@@ -3620,7 +3620,7 @@
"option": {}, "option": {},
"card": { "card": {
"vpn": { "vpn": {
"countConnected": "" "countConnected": "{count} connected"
} }
}, },
"error": { "error": {
@@ -3637,14 +3637,14 @@
"name": "" "name": ""
}, },
"wifi": { "wifi": {
"name": "" "name": "Wi-Fi"
} }
}, },
"users": { "users": {
"label": "" "label": ""
}, },
"guests": { "guests": {
"label": "" "label": "Guests"
} }
}, },
"option": { "option": {
@@ -3890,7 +3890,7 @@
"option": { "option": {
"cpu": "", "cpu": "",
"memory": "", "memory": "",
"gpu": "", "gpu": "GPU",
"network": "" "network": ""
} }
}, },
@@ -3908,7 +3908,7 @@
"cpu": "", "cpu": "",
"memory": "", "memory": "",
"gpu": "", "gpu": "",
"network": "", "network": "NET",
"up": "", "up": "",
"down": "" "down": ""
} }
@@ -4956,7 +4956,7 @@
"title": "", "title": "",
"status": { "status": {
"idle": "", "idle": "",
"running": "", "running": "Running",
"error": "", "error": "",
"disabled": "" "disabled": ""
}, },
@@ -5858,7 +5858,7 @@
} }
}, },
"search": { "search": {
"placeholder": "", "placeholder": "Search for anything",
"nothingFound": "", "nothingFound": "",
"error": { "error": {
"fetch": "" "fetch": ""
+6 -6
View File
@@ -730,15 +730,15 @@
"iconUrl": "URL del icono", "iconUrl": "URL del icono",
"url": "URL", "url": "URL",
"authType": "Tipo de autenticación", "authType": "Tipo de autenticación",
"headerName": "", "headerName": "Nombre del parámetro de cabecera/consulta",
"method": "Método", "method": "Método",
"requestBody": "", "requestBody": "Cuerpo de la Solicitud",
"displayType": "Tipo de visualización", "displayType": "Tipo de visualización",
"jsonPath": "", "jsonPath": "JSON Path",
"jsonPathHint": "", "jsonPathHint": "Usa la sintaxis JSONPath para extraer valores, por ejemplo, $.data.temperature o $.items[0].name",
"label": "", "label": "Nombre",
"unit": "Unidad", "unit": "Unidad",
"mappings": "", "mappings": "Asignaciones",
"tablePath": "", "tablePath": "",
"tablePathHint": "", "tablePathHint": "",
"columns": "", "columns": "",
+7 -7
View File
@@ -604,7 +604,7 @@
} }
}, },
"app": { "app": {
"search": "Trouver une application", "search": "Trouver une app",
"page": { "page": {
"list": { "list": {
"title": "Applications", "title": "Applications",
@@ -3358,7 +3358,7 @@
"option": {}, "option": {},
"titles": { "titles": {
"stats": { "stats": {
"main": "Statistiques des médias", "main": "Stats des médias",
"approved": "Déjà approuvé", "approved": "Déjà approuvé",
"pending": "En attente de validation", "pending": "En attente de validation",
"processing": "En cours de traitement", "processing": "En cours de traitement",
@@ -4118,7 +4118,7 @@
"internalServerError": "" "internalServerError": ""
}, },
"history": "Historique", "history": "Historique",
"resolved": "Résolut", "resolved": "Résolu",
"status": { "status": {
"triggered": "", "triggered": "",
"ok": "" "ok": ""
@@ -4173,12 +4173,12 @@
"chart": { "chart": {
"cpu": { "cpu": {
"title": "", "title": "",
"subtitle": "", "subtitle": "Utilisation moyenne du CPU à l'échelle du système",
"series": "CPU" "series": "CPU"
}, },
"memory": { "memory": {
"title": "Utilisation Mémoire", "title": "Utilisation Mémoire",
"subtitle": "", "subtitle": "Utilisation précise au moment enregistré",
"series": "", "series": "",
"cache": "" "cache": ""
}, },
@@ -4189,7 +4189,7 @@
}, },
"diskIO": { "diskIO": {
"title": "", "title": "",
"subtitle": "", "subtitle": "Débit du système de fichiers root",
"read": "", "read": "",
"write": "" "write": ""
}, },
@@ -5766,7 +5766,7 @@
"label": "Médias" "label": "Médias"
}, },
"apps": { "apps": {
"label": "Applications", "label": "Apps",
"new": { "new": {
"label": "Nouveau" "label": "Nouveau"
}, },
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -3452,7 +3452,7 @@
"queued": "" "queued": ""
}, },
"type": { "type": {
"movie": "", "movie": "Film",
"episode": "" "episode": ""
}, },
"empty": { "empty": {
+2 -2
View File
@@ -4887,11 +4887,11 @@
"text": "Не індексувати сайт у пошукових системах і не показувати його в жодному результаті пошуку" "text": "Не індексувати сайт у пошукових системах і не показувати його в жодному результаті пошуку"
}, },
"noFollow": { "noFollow": {
"title": "Не переходити (No follow)", "title": "Не переходити",
"text": "Не переходьте за посиланнями під час індексації. Якщо вимкнути це, то сканери намагатимуться переходити за всіма посиланнями на Homarr." "text": "Не переходьте за посиланнями під час індексації. Якщо вимкнути це, то сканери намагатимуться переходити за всіма посиланнями на Homarr."
}, },
"noTranslate": { "noTranslate": {
"title": "Не перекладати (No translate)", "title": "Не перекладати",
"text": "Якщо мова сайту не є тією, яку користувач, імовірно, захоче прочитати, Google покаже посилання на переклад у результатах пошуку" "text": "Якщо мова сайту не є тією, яку користувач, імовірно, захоче прочитати, Google покаже посилання на переклад у результатах пошуку"
}, },
"noSiteLinksSearchBox": { "noSiteLinksSearchBox": {
+1 -1
View File
@@ -56,7 +56,7 @@ export default function FirewallWidget({ integrationIds, width, itemId }: Widget
const t = useI18n(); const t = useI18n();
return ( return (
<ScrollArea h="100%"> <ScrollArea h="100%" style={{ minHeight: 0 }}>
<Group justify="space-between" w="100%" style={{ padding: "8px" }}> <Group justify="space-between" w="100%" style={{ padding: "8px" }}>
<FirewallMenu <FirewallMenu
onChange={handleSelect} onChange={handleSelect}
+33 -10
View File
@@ -418,8 +418,8 @@ catalogs:
specifier: 16.3.0-preview.9 specifier: 16.3.0-preview.9
version: 16.3.0-preview.9 version: 16.3.0-preview.9
next-auth: next-auth:
specifier: 5.0.0-beta.31 specifier: 5.0.0-beta.32
version: 5.0.0-beta.31 version: 5.0.0-beta.32
next-intl: next-intl:
specifier: 4.13.1 specifier: 4.13.1
version: 4.13.1 version: 4.13.1
@@ -567,7 +567,7 @@ overrides:
brace-expansion@>=1.0.0 <=1.1.11: '>=5.0.7' brace-expansion@>=1.0.0 <=1.1.11: '>=5.0.7'
esbuild@<=0.24.2: '>=0.28.1' esbuild@<=0.24.2: '>=0.28.1'
form-data@>=4.0.0 <4.0.4: '>=4.0.6' form-data@>=4.0.0 <4.0.4: '>=4.0.6'
hono@<4.6.5: '>=4.12.28' hono@<4.6.5: '>=4.13.7'
linkifyjs@<4.3.2: '>=4.3.3' linkifyjs@<4.3.2: '>=4.3.3'
nanoid@>=4.0.0 <5.0.9: '>=5.1.16' nanoid@>=4.0.0 <5.0.9: '>=5.1.16'
prismjs@<1.30.0: '>=1.30.0' prismjs@<1.30.0: '>=1.30.0'
@@ -1401,7 +1401,7 @@ importers:
version: 16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0) version: 16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
next-auth: next-auth:
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0-beta.31(next@16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7) version: 5.0.0-beta.32(next@16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)
react: react:
specifier: 'catalog:' specifier: 'catalog:'
version: 19.2.7 version: 19.2.7
@@ -2872,6 +2872,20 @@ packages:
nodemailer: nodemailer:
optional: true optional: true
'@auth/core@0.41.3':
resolution: {integrity: sha512-sJ3JMHHkXMD3aOjopv7mOBTO1Ocw4b0fAEXJBz6k7YHLpYQI6C40jCUPc5fNvUKxXRXNE1/sRISA15UrwWJBTw==}
peerDependencies:
'@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2
nodemailer: ^7.0.7 || ^8.0.5
peerDependenciesMeta:
'@simplewebauthn/browser':
optional: true
'@simplewebauthn/server':
optional: true
nodemailer:
optional: true
'@auth/drizzle-adapter@1.11.2': '@auth/drizzle-adapter@1.11.2':
resolution: {integrity: sha512-VOuj7REI8jfJjpSbsYwDM/Zrn55T6lS9Yc+29V+EXMcel8eqG7x+7LodNfd1WHjakfkLYi+qsbYUHB3E6aDA4w==} resolution: {integrity: sha512-VOuj7REI8jfJjpSbsYwDM/Zrn55T6lS9Yc+29V+EXMcel8eqG7x+7LodNfd1WHjakfkLYi+qsbYUHB3E6aDA4w==}
@@ -5020,7 +5034,7 @@ packages:
resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==}
engines: {node: '>=18.14.1'} engines: {node: '>=18.14.1'}
peerDependencies: peerDependencies:
hono: '>=4.12.28' hono: '>=4.13.7'
'@iconify/types@2.0.0': '@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -9407,6 +9421,7 @@ packages:
crypto-js@4.2.0: crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained.
crypto-random-string@4.0.0: crypto-random-string@4.0.0:
resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==}
@@ -12253,13 +12268,13 @@ packages:
neverpanic@0.0.8: neverpanic@0.0.8:
resolution: {integrity: sha512-vVdkelrLxaow/fdWDumzNBO+jwm6X8bxeLJc34THtpj70u0C5QBkcV6CRCu2X726km7XD45N0A3QtYCla4RvKw==} resolution: {integrity: sha512-vVdkelrLxaow/fdWDumzNBO+jwm6X8bxeLJc34THtpj70u0C5QBkcV6CRCu2X726km7XD45N0A3QtYCla4RvKw==}
next-auth@5.0.0-beta.31: next-auth@5.0.0-beta.32:
resolution: {integrity: sha512-1OBgCKPzo+S7UWWMp3xgvGvIJ0OpV7B3vR4ZDRqD9a4Ch+OT6dakLXG9ivhtmIWVa71nTSXattOHyCg8sNi8/Q==} resolution: {integrity: sha512-CGlChIEWZ6LltNVxrE5yiySMID+Idpmry47JYA5lLwgD8Sx02a8M65VL0TWVz9nbnOioS/tCW/rP/0+mE7Qp4Q==}
peerDependencies: peerDependencies:
'@simplewebauthn/browser': ^9.0.1 '@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2 '@simplewebauthn/server': ^9.0.2
next: ^14.0.0-0 || ^15.0.0 || ^16.0.0 next: ^14.0.0-0 || ^15.0.0 || ^16.0.0
nodemailer: ^7.0.7 nodemailer: ^7.0.7 || ^8.0.5
react: ^18.2.0 || ^19.0.0 react: ^18.2.0 || ^19.0.0
peerDependenciesMeta: peerDependenciesMeta:
'@simplewebauthn/browser': '@simplewebauthn/browser':
@@ -15786,6 +15801,14 @@ snapshots:
preact: 10.24.3 preact: 10.24.3
preact-render-to-string: 6.5.11(preact@10.24.3) preact-render-to-string: 6.5.11(preact@10.24.3)
'@auth/core@0.41.3':
dependencies:
'@panva/hkdf': 1.2.1
jose: 6.2.3
oauth4webapi: 3.3.0
preact: 10.24.3
preact-render-to-string: 6.5.11(preact@10.24.3)
'@auth/drizzle-adapter@1.11.2': '@auth/drizzle-adapter@1.11.2':
dependencies: dependencies:
'@auth/core': 0.41.2 '@auth/core': 0.41.2
@@ -26791,9 +26814,9 @@ snapshots:
neverpanic@0.0.8: {} neverpanic@0.0.8: {}
next-auth@5.0.0-beta.31(next@16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7): next-auth@5.0.0-beta.32(next@16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7):
dependencies: dependencies:
'@auth/core': 0.41.2 '@auth/core': 0.41.3
next: 16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0) next: 16.3.0-preview.9(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
react: 19.2.7 react: 19.2.7
+8 -2
View File
@@ -146,7 +146,7 @@ catalog:
maria2: ^0.4.1 maria2: ^0.4.1
mysql2: 3.22.6 mysql2: 3.22.6
next: 16.3.0-preview.9 next: 16.3.0-preview.9
next-auth: 5.0.0-beta.31 next-auth: 5.0.0-beta.32
next-intl: 4.13.1 next-intl: 4.13.1
node-loader: ^2.1.0 node-loader: ^2.1.0
octokit: ^5.0.5 octokit: ^5.0.5
@@ -210,6 +210,12 @@ minimumReleaseAgeExclude:
- undici@7.28.0 - undici@7.28.0
# Renovate security update: esbuild@0.28.1 # Renovate security update: esbuild@0.28.1
- esbuild@0.28.1 - esbuild@0.28.1
# Renovate security update: @auth/core@0.41.3
- "@auth/core@0.41.3"
# Renovate security update: hono@4.13.7
- hono@4.13.7
# Renovate security update: next-auth@5.0.0-beta.32
- next-auth@5.0.0-beta.32
overrides: overrides:
"@babel/helpers@<7.26.10": ">=7.29.7" "@babel/helpers@<7.26.10": ">=7.29.7"
"@babel/runtime@<7.26.10": ">=7.29.7" "@babel/runtime@<7.26.10": ">=7.29.7"
@@ -218,7 +224,7 @@ overrides:
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.7" "brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.7"
"esbuild@<=0.24.2": ">=0.28.1" "esbuild@<=0.24.2": ">=0.28.1"
"form-data@>=4.0.0 <4.0.4": ">=4.0.6" "form-data@>=4.0.0 <4.0.4": ">=4.0.6"
"hono@<4.6.5": ">=4.12.28" "hono@<4.6.5": ">=4.13.7"
"linkifyjs@<4.3.2": ">=4.3.3" "linkifyjs@<4.3.2": ">=4.3.3"
"nanoid@>=4.0.0 <5.0.9": ">=5.1.16" "nanoid@>=4.0.0 <5.0.9": ">=5.1.16"
"prismjs@<1.30.0": ">=1.30.0" "prismjs@<1.30.0": ">=1.30.0"
+6 -5
View File
@@ -21,20 +21,18 @@ fi
export AUTH_SECRET=$(openssl rand -base64 32) export AUTH_SECRET=$(openssl rand -base64 32)
# Start nginx proxy # Start nginx proxy
# 1. Replace the HOSTNAME in the nginx template file # 1. Create the nginx configuration file from the template
# 2. Create the nginx configuration file from the template # 2. Start the nginx server
# 3. Start the nginx server
# Only listen on IPv6 when the host actually has IPv6 configured (#4596). # Only listen on IPv6 when the host actually has IPv6 configured (#4596).
# Otherwise nginx aborts with "socket() [::]:7575 failed (97: Address family # Otherwise nginx aborts with "socket() [::]:7575 failed (97: Address family
# not supported by protocol)". Check HOMARR_DISABLE_IPV6 first so users can # not supported by protocol)". Check HOMARR_DISABLE_IPV6 first so users can
# force IPv4-only even when the host supports IPv6, then probe for IPv6 # force IPv4-only even when the host supports IPv6, then probe for IPv6
# addresses via grep (not -s: proc files always report size 0). # addresses via grep (not -s: proc files always report size 0).
export HOSTNAME
export NGINX_LISTEN_IPV6='' export NGINX_LISTEN_IPV6=''
if [ "${HOMARR_DISABLE_IPV6:-false}" != "true" ] && grep -q . /proc/net/if_inet6 2>/dev/null; then if [ "${HOMARR_DISABLE_IPV6:-false}" != "true" ] && grep -q . /proc/net/if_inet6 2>/dev/null; then
NGINX_LISTEN_IPV6='listen [::]:7575;' NGINX_LISTEN_IPV6='listen [::]:7575;'
fi fi
envsubst '${HOSTNAME} ${NGINX_LISTEN_IPV6}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf envsubst '${NGINX_LISTEN_IPV6}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
# Start services in the background and store their PIDs # Start services in the background and store their PIDs
nginx -g 'daemon off;' & nginx -g 'daemon off;' &
NGINX_PID=$! NGINX_PID=$!
@@ -67,6 +65,9 @@ terminate() {
trap terminate TERM INT trap terminate TERM INT
# Next.js standalone uses HOSTNAME as its bind address. Docker's generated
# hostname can be unresolvable, so bind explicitly while nginx uses loopback.
export HOSTNAME=0.0.0.0
node apps/nextjs/server.js & node apps/nextjs/server.js &
NEXTJS_PID=$! NEXTJS_PID=$!
+1 -1
View File
@@ -1 +1 @@
[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"marektancak0106","avatar_url":"https://avatars.githubusercontent.com/u/25422667?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":"beanbag420","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.githubusercontentLine truncated [{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"marektancak0106","avatar_url":"https://avatars.githubusercontent.com/u/25422667?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":"beanbag420","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.githubusercontentLine truncated
+1 -1
View File
@@ -1 +1 @@
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"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/aaa1f083e04c572f953fde4563171e4a.jpeg"},{"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://crowLine truncated [{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"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/aaa1f083e04c572f953fde4563171e4a.jpeg"},{"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://crowLine truncated