Compare commits

..
Author SHA1 Message Date
ajnart 84f7ed97fc style: auto-fix lint and format 2026-06-17 14:11:52 +00:00
Thomas Camlong dc1e1891fb fix: persist Beszel server selection and improve picker visibility
The server picker was invisible (opacity: 0, hover-only) and selections
were lost on refresh because setOptions only updated the React Query cache.
Add a generic saveItemOptions mutation that persists widget option changes
directly to the database, and replace the hidden icon with a visible button.
2026-06-17 16:09:32 +02:00
82 changed files with 2000 additions and 2970 deletions

No files matched your search

-2
View File
@@ -33,8 +33,6 @@ body:
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
- 1.66.1
- 1.66.0
- 1.65.0
- 1.64.0
- 1.62.0
@@ -23,7 +23,6 @@ Homarr supports multiple authentication options, from internal userbase (credent
| AUTH_PROVIDERS | Select Which provider to use between *credentials*, *ldap* and *oidc*. <br /> Multiple providers can be enabled with by separating them with `,`, (ex. `AUTH_PROVIDERS=credentials,oidc`, it is highly recommended to just enable one provider). | credentials |
| AUTH_LOGOUT_REDIRECT_URL | URL to redirect to after clicking logging out. | --- |
| AUTH_SESSION_EXPIRY_TIME | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") | "30d" |
| AUTH_COOKIE_PREFIX | Prefix used for all authentication cookies (session, csrf, callback, pkce, state, nonce). Change this if you run another Auth.js/NextAuth app on the same hostname as Homarr (browsers scope cookies by host, not by port) to avoid cookie name collisions. Only letters, numbers, hyphens and underscores are allowed. | `homarr` |
<Tabs>
<TabItem value="credentials" label="Credentials Provider" default>
@@ -395,7 +395,6 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
+2 -2
View File
@@ -32,7 +32,7 @@
"@scalar/api-reference-react": "catalog:",
"@signalwire/docusaurus-plugin-llms-txt": "^1.2.2",
"@tabler/icons-react": "catalog:",
"@tailwindcss/postcss": "^4.3.1",
"@tailwindcss/postcss": "^4.3.0",
"clsx": "catalog:",
"dayjs": "catalog:",
"docusaurus-plugin-image-zoom": "^3.0.1",
@@ -44,7 +44,7 @@
"react": "catalog:",
"react-dom": "catalog:",
"swiper": "^12.2.0",
"tailwindcss": "^4.3.1"
"tailwindcss": "^4.3.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.10.1",
-1
View File
@@ -89,7 +89,6 @@
"mcp-handler": "catalog:",
"next": "catalog:",
"postcss-preset-mantine": "catalog:",
"posthog-js": "catalog:",
"prismjs": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
+1 -1
View File
@@ -139,11 +139,11 @@ export default async function Layout(props: {
suppressHydrationWarning
>
<head>
<Analytics />
<SearchEngineOptimization />
<CrowdinLiveTranslation locale={locale} />
</head>
<body className={["font-sans", fontSans.variable].join(" ")}>
<Analytics enabled={serverSettings.analytics.enableGeneral} />
<StackedProvider>
<Notifications pauseResetOnHover="notification" />
<ServiceWorkerRegistration />
@@ -17,10 +17,9 @@ interface AnalyticsSettingsProps {
export const AnalyticsSettings = ({ initialData }: AnalyticsSettingsProps) => {
const t = useScopedI18n("management.page.settings.section.analytics");
const { instanceId: _, ...formDefaults } = initialData;
const form = useForm({
initialValues: formDefaults,
onValuesChange: (updatedValues, _prev) => {
initialValues: initialData,
onValuesChange: (updatedValues, _) => {
if (!form.isValid()) {
return;
}
+11 -19
View File
@@ -1,24 +1,16 @@
"use client";
import Script from "next/script";
import { useEffect } from "react";
import { UMAMI_WEBSITE_ID } from "@homarr/analytics";
import { db } from "@homarr/db";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
const POSTHOG_KEY = "phc_vYBmGWNbRshvfeC7EHfeSmUm2pD2Neg5nGqzJuGvS8Hs";
const POSTHOG_HOST = "https://hog.homarr.dev";
export const Analytics = async () => {
// For static pages it will not find any analytics data so we do not include the script on them
const analytics = await getServerSettingByKeyAsync(db, "analytics").catch(() => null);
export const Analytics = ({ enabled }: { enabled: boolean }) => {
useEffect(() => {
if (!enabled) return;
if (analytics?.enableGeneral) {
return <Script src="https://umami.homarr.dev/script.js" data-website-id={UMAMI_WEBSITE_ID} defer />;
}
void import("posthog-js").then(({ default: posthog }) => {
posthog.init(POSTHOG_KEY, {
api_host: POSTHOG_HOST,
capture_pageview: false,
capture_pageleave: false,
autocapture: false,
persistence: "memory",
});
});
}, [enabled]);
return null;
return <></>;
};
+1 -1
View File
@@ -64,7 +64,7 @@
"node": ">=24.16.0",
"pnpm": ">=11.6.0"
},
"packageManager": "pnpm@11.7.0",
"packageManager": "pnpm@11.6.0",
"dependencies": {
"jsonpath-plus": "catalog:"
}
+3 -1
View File
@@ -26,7 +26,9 @@
"@homarr/core": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/docker": "workspace:^0.1.0",
"posthog-node": "catalog:"
"@homarr/server-settings": "workspace:^0.1.0",
"@umami/node": "catalog:",
"superjson": "catalog:"
},
"devDependencies": {
"@homarr/tsconfig": "workspace:^0.1.0",
-23
View File
@@ -1,23 +0,0 @@
import { PostHog } from "posthog-node";
export const POSTHOG_API_KEY = "phc_vYBmGWNbRshvfeC7EHfeSmUm2pD2Neg5nGqzJuGvS8Hs";
export const POSTHOG_HOST = "https://hog.homarr.dev";
let instance: PostHog | undefined;
export const getPostHogClient = (): PostHog => {
instance ??= new PostHog(POSTHOG_API_KEY, {
host: POSTHOG_HOST,
flushAt: 10,
flushInterval: 30_000,
});
return instance;
};
export const trackEvent = (instanceId: string, event: string, properties?: Record<string, unknown>) => {
getPostHogClient().capture({
distinctId: instanceId,
event,
properties,
});
};
+2
View File
@@ -0,0 +1,2 @@
export const UMAMI_HOST_URL = "https://umami.homarr.dev";
export const UMAMI_WEBSITE_ID = "ff7dc470-a84f-4779-b1ab-66a5bb16a94b";
+2 -2
View File
@@ -1,2 +1,2 @@
export { trackEvent } from "./client";
export { sendServerAnalyticsAsync } from "./send-server-analytics";
export * from "./constants";
export * from "./send-server-analytics";
+63 -136
View File
@@ -1,16 +1,17 @@
import type { UmamiEventData } from "@umami/node";
import { Umami } from "@umami/node";
import { isProviderEnabled } from "@homarr/auth/server";
import { createId, Stopwatch } from "@homarr/common";
import { Stopwatch } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { count, db } from "@homarr/db";
import { isMysql, isPostgresql } from "@homarr/db/collection";
import { getServerSettingByKeyAsync, updateServerSettingByKeyAsync } from "@homarr/db/queries";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import {
accounts,
apiKeys,
apps,
boards,
cronJobConfigurations,
customWidgetDefinitions,
groups,
iconRepositories,
icons,
@@ -23,160 +24,86 @@ import {
searchEngines,
sectionLayouts,
sections,
sessions,
trustedCertificateHostnames,
users,
} from "@homarr/db/schema";
import { env as dockerEnv } from "@homarr/docker/env";
import packageJson from "../../../package.json";
import { getPostHogClient } from "./client";
import { UMAMI_HOST_URL, UMAMI_WEBSITE_ID } from "./constants";
const logger = createLogger({ module: "analytics" });
type AnalyticsResult = "sent" | "disabled" | "failed";
// ponytail: no DB-level lock; concurrent calls may both generate an ID, but the last write wins
// and the ID stabilizes after the first successful run. Upgrade path: use DB upsert with WHERE instanceId IS NULL.
const getOrCreateInstanceId = async (
analyticsSettings: Awaited<ReturnType<typeof getServerSettingByKeyAsync<"analytics">>>,
): Promise<string> => {
if (analyticsSettings.instanceId) return analyticsSettings.instanceId;
const instanceId = createId();
await updateServerSettingByKeyAsync(db, "analytics", { ...analyticsSettings, instanceId });
const verified = await getServerSettingByKeyAsync(db, "analytics");
return verified.instanceId ?? instanceId;
const getDatabaseType = (): "mysql" | "postgresql" | "sqlite" => {
if (isMysql()) return "mysql";
if (isPostgresql()) return "postgresql";
return "sqlite";
};
const sumGroupedCounts = (rows: { count: number }[]): number => rows.reduce((sum, row) => sum + row.count, 0);
const getEnabledAuthProviders = (): string[] => {
const providers: string[] = [];
if (isProviderEnabled("credentials")) providers.push("credentials");
if (isProviderEnabled("oidc")) providers.push("oidc");
if (isProviderEnabled("ldap")) providers.push("ldap");
return providers;
};
export const sendServerAnalyticsAsync = async (): Promise<AnalyticsResult> => {
export const sendServerAnalyticsAsync = async () => {
const stopWatch = new Stopwatch();
const analyticsSettings = await getServerSettingByKeyAsync(db, "analytics");
if (!analyticsSettings.enableGeneral) {
logger.info("Analytics are disabled. No data will be sent. Enable analytics in the settings");
return "disabled";
return;
}
const client = getPostHogClient();
const umamiInstance = new Umami();
umamiInstance.init({
hostUrl: UMAMI_HOST_URL,
websiteId: UMAMI_WEBSITE_ID,
});
try {
const instanceId = await getOrCreateInstanceId(analyticsSettings);
const enabledAuthProviders = getEnabledAuthProviders();
const [
cultureSettings,
countBoards,
countGroups,
countApps,
countSections,
countSearchEngines,
countIconRepositories,
countIcons,
countLayouts,
countItemLayouts,
countSectionLayouts,
countCronJobConfigs,
countCustomWidgets,
countTrustedCertificates,
countUsers,
countApiKeys,
countInvites,
countMedias,
countSessions,
countAccounts,
integrationKinds,
widgetKinds,
] = await Promise.all([
getServerSettingByKeyAsync(db, "culture"),
db.$count(boards),
db.$count(groups),
db.$count(apps),
db.$count(sections),
db.$count(searchEngines),
db.$count(iconRepositories),
db.$count(icons),
db.$count(layouts),
db.$count(itemLayouts),
db.$count(sectionLayouts),
db.$count(cronJobConfigurations),
db.$count(customWidgetDefinitions),
db.$count(trustedCertificateHostnames),
db.$count(users),
db.$count(apiKeys),
db.$count(invites),
db.$count(medias),
db.$count(sessions),
db.$count(accounts),
db
.select({ kind: integrations.kind, count: count(integrations.id) })
.from(integrations)
.groupBy(integrations.kind),
db
.select({ kind: items.kind, count: count(items.id) })
.from(items)
.groupBy(items.kind),
]);
const analyticsData: UmamiEventData = {
homarrVersion: packageJson.version,
databaseType: getDatabaseType(),
dockerEnabled: dockerEnv.ENABLE_DOCKER ? 1 : 0,
kubernetesEnabled: dockerEnv.ENABLE_KUBERNETES ? 1 : 0,
authCredentials: enabledAuthProviders.includes("credentials") ? 1 : 0,
authOidc: enabledAuthProviders.includes("oidc") ? 1 : 0,
authLdap: enabledAuthProviders.includes("ldap") ? 1 : 0,
countUsers: await db.$count(users),
countBoards: await db.$count(boards),
countGroups: await db.$count(groups),
countApps: await db.$count(apps),
countWidgets: await db.$count(items),
countSections: await db.$count(sections),
countIntegrations: await db.$count(integrations),
countSearchEngines: await db.$count(searchEngines),
countIconRepositories: await db.$count(iconRepositories),
countIcons: await db.$count(icons),
countApiKeys: await db.$count(apiKeys),
countInvites: await db.$count(invites),
countMedias: await db.$count(medias),
countLayouts: await db.$count(layouts),
countItemLayouts: await db.$count(itemLayouts),
countSectionLayouts: await db.$count(sectionLayouts),
countCronJobConfigs: await db.$count(cronJobConfigurations),
};
const enabledAuthProviders = (["credentials", "oidc", "ldap"] as const).filter(isProviderEnabled);
const integrationKinds = await db
.select({ kind: integrations.kind, count: count(integrations.id) })
.from(integrations)
.groupBy(integrations.kind);
const properties: Record<string, unknown> = {
homarrVersion: packageJson.version,
databaseType: isMysql() ? "mysql" : isPostgresql() ? "postgresql" : "sqlite",
dockerEnabled: Boolean(dockerEnv.ENABLE_DOCKER),
kubernetesEnabled: Boolean(dockerEnv.ENABLE_KUBERNETES),
authProviders: enabledAuthProviders,
integrationKinds.forEach((integrationKind) => {
analyticsData[`integration_${integrationKind.kind}`] = integrationKind.count;
});
osPlatform: process.platform,
osArch: process.arch,
uptimeSeconds: Math.floor(process.uptime()),
defaultLocale: cultureSettings.defaultLocale,
countBoards,
countGroups,
countApps,
countSections,
countSearchEngines,
countIconRepositories,
countIcons,
countLayouts,
countItemLayouts,
countSectionLayouts,
countCronJobConfigs,
countCustomWidgets,
countTrustedCertificates,
countUsers,
countApiKeys,
countInvites,
countMedias,
countSessions,
countAccounts,
countIntegrations: sumGroupedCounts(integrationKinds),
countWidgets: sumGroupedCounts(widgetKinds),
};
for (const row of integrationKinds) {
properties[`integration_${row.kind}`] = row.count;
}
for (const row of widgetKinds) {
properties[`widget_${row.kind}`] = row.count;
}
client.capture({
distinctId: instanceId,
event: "server-analytics",
properties,
});
await client.flush();
logger.info(`Sent analytics to PostHog in ${stopWatch.getElapsedInHumanWords()}`);
return "sent";
} catch (error) {
logger.warn("Failed to send analytics to PostHog", { error });
return "failed";
const response = await umamiInstance.track("server-analytics-v2", analyticsData);
if (!response.ok) {
logger.warn("Unable to send analytics to Umami instance");
}
logger.info(`Sent analytics in ${stopWatch.getElapsedInHumanWords()}`);
};
-1
View File
@@ -24,7 +24,6 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@homarr/analytics": "workspace:^0.1.0",
"@homarr/auth": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
-1
View File
@@ -28,7 +28,6 @@ export const appRouter = createTRPCRouter({
updateChecker: lazy(() => import("./router/update-checker").then((mod) => mod.updateCheckerRouter)),
certificates: lazy(() => import("./router/certificates/certificate-router").then((mod) => mod.certificateRouter)),
bangs: lazy(() => import("./router/bangs/bangs-router").then((mod) => mod.bangsRouter)),
analytics: lazy(() => import("./router/analytics").then((mod) => mod.analyticsRouter)),
info: lazy(() => import("./router/info").then((mod) => mod.infoRouter)),
customWidget: lazy(() => import("./router/custom-widget/custom-widget-router").then((mod) => mod.customWidgetRouter)),
});
-51
View File
@@ -1,51 +0,0 @@
import { TRPCError } from "@trpc/server";
import z from "zod/v4";
import { sendServerAnalyticsAsync, trackEvent } from "@homarr/analytics";
import { env } from "@homarr/common/env";
import { db } from "@homarr/db";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure } from "../trpc";
const getInstanceId = async (): Promise<string | null> => {
if (env.NO_EXTERNAL_CONNECTION) return null;
const settings = await getServerSettingByKeyAsync(db, "analytics");
if (!settings.enableGeneral) return null;
return settings.instanceId;
};
export const analyticsRouter = createTRPCRouter({
sendAnalytics: permissionRequiredProcedure.requiresPermission("admin").mutation(async () => {
if (env.NO_EXTERNAL_CONNECTION) {
throw new TRPCError({ code: "PRECONDITION_FAILED", message: "External connections are disabled" });
}
return { status: await sendServerAnalyticsAsync() };
}),
trackFeature: protectedProcedure
.input(
z.object({
feature: z.string().max(128),
properties: z.record(z.string(), z.unknown()).optional(),
}),
)
.mutation(async ({ input, ctx }) => {
const instanceId = await getInstanceId();
if (!instanceId) return;
trackEvent(instanceId, `feature:${input.feature}`, {
...input.properties,
userId: ctx.session.user.id,
});
}),
heartbeat: protectedProcedure.mutation(async ({ ctx }) => {
const instanceId = await getInstanceId();
if (!instanceId) return;
trackEvent(instanceId, "user-active", {
userId: ctx.session.user.id,
});
}),
});
@@ -27,7 +27,6 @@ import {
integrationSecretKindObject,
} from "@homarr/definitions";
import { createIntegrationAsync } from "@homarr/integrations";
import { invalidateIntegrationCacheAsync } from "@homarr/redis";
import { byIdSchema } from "@homarr/validation/common";
import {
integrationCreateSchema,
@@ -470,10 +469,6 @@ export const integrationRouter = createTRPCRouter({
kind: integration.kind,
url: input.url,
});
// Invalidate all cached data for this integration so that widgets pick up the
// new configuration immediately instead of serving stale (or errored) data.
await invalidateIntegrationCacheAsync(input.id);
}),
delete: protectedProcedure
.meta({
@@ -495,9 +490,6 @@ export const integrationRouter = createTRPCRouter({
}
await ctx.db.delete(integrations).where(eq(integrations.id, input.id));
// Clean up any cached data left behind by the deleted integration.
await invalidateIntegrationCacheAsync(input.id);
}),
getIntegrationPermissions: protectedProcedure.input(byIdSchema).query(async ({ input, ctx }) => {
await throwIfActionForbiddenAsync(ctx, eq(integrations.id, input.id), "full");
+6 -7
View File
@@ -24,18 +24,17 @@ export const serverSettingsRouter = createTRPCRouter({
}),
)
.mutation(async ({ ctx, input }) => {
const current = await getServerSettingByKeyAsync(ctx.db, input.settingsKey);
await updateServerSettingByKeyAsync(ctx.db, input.settingsKey, {
...current,
...input.value,
} as ServerSettings[keyof ServerSettings]);
await updateServerSettingByKeyAsync(
ctx.db,
input.settingsKey,
input.value as ServerSettings[keyof ServerSettings],
);
}),
initSettings: onboardingProcedure
.requiresStep("settings")
.input(settingsInitSchema)
.mutation(async ({ ctx, input }) => {
const currentAnalytics = await getServerSettingByKeyAsync(ctx.db, "analytics");
await updateServerSettingByKeyAsync(ctx.db, "analytics", { ...currentAnalytics, ...input.analytics });
await updateServerSettingByKeyAsync(ctx.db, "analytics", input.analytics);
await updateServerSettingByKeyAsync(ctx.db, "crawlingAndIndexing", input.crawlingAndIndexing);
await nextOnboardingStepAsync(ctx.db, undefined);
}),
@@ -75,6 +75,9 @@ describe("saveSettings", () => {
settingsKey: "analytics",
value: {
enableGeneral: true,
enableWidgetData: true,
enableIntegrationData: true,
enableUserData: true,
},
});
@@ -84,7 +87,9 @@ describe("saveSettings", () => {
settingKey: "analytics",
value: SuperJSON.stringify({
enableGeneral: true,
instanceId: null,
enableWidgetData: true,
enableIntegrationData: true,
enableUserData: true,
}),
},
]);
+5 -19
View File
@@ -20,24 +20,6 @@ import { expireDateAfter, generateSessionToken, sessionTokenCookieName } from ".
const logger = createLogger({ module: "authConfiguration" });
// All Auth.js cookies are prefixed with AUTH_COOKIE_PREFIX (default "homarr") so
// Homarr's cookie jar is isolated from any other Auth.js/NextAuth app on the
// same hostname (browsers scope cookies by host, not by port).
// See https://github.com/homarr-labs/homarr/issues/5773
const createCookies = (useSecureCookies: boolean) => {
const prefix = env.AUTH_COOKIE_PREFIX;
const securePrefix = useSecureCookies ? "__Secure-" : "";
const hostPrefix = useSecureCookies ? "__Host-" : "";
return {
sessionToken: { name: sessionTokenCookieName },
csrfToken: { name: `${hostPrefix}${prefix}.csrf-token` },
callbackUrl: { name: `${securePrefix}${prefix}.callback-url` },
pkceCodeVerifier: { name: `${securePrefix}${prefix}.pkce.cooldown` },
state: { name: `${securePrefix}${prefix}.state` },
nonce: { name: `${securePrefix}${prefix}.nonce` },
};
};
// See why it's unknown in the [...nextauth]/route.ts file
export const createConfiguration = (
provider: SupportedAuthProvider | "unknown",
@@ -59,7 +41,11 @@ export const createConfiguration = (
},
},
trustHost: true,
cookies: createCookies(useSecureCookies),
cookies: {
sessionToken: {
name: sessionTokenCookieName,
},
},
adapter,
providers: filterProviders([
Credentials(createCredentialsConfiguration(db)),
-5
View File
@@ -28,11 +28,6 @@ export const env = createEnv({
AUTH_LOGOUT_REDIRECT_URL: z.string().url().optional(),
AUTH_SESSION_EXPIRY_TIME: createDurationSchema("30d"),
AUTH_PROVIDERS: authProvidersSchema,
AUTH_COOKIE_PREFIX: z
.string()
.min(1)
.regex(/^[a-zA-Z0-9-_]+$/, "AUTH_COOKIE_PREFIX must only contain letters, numbers, hyphens and underscores")
.default("homarr"),
...(authProviders.includes("oidc")
? {
AUTH_OIDC_ISSUER: z.string().url(),
+2 -4
View File
@@ -4,11 +4,9 @@ import { generateSecureRandomToken } from "@homarr/common/server";
import type { Database } from "@homarr/db";
import { getCurrentUserPermissionsAsync } from "./callbacks";
import { env } from "./env";
// Prefixed (AUTH_COOKIE_PREFIX, default "homarr") to avoid cookie collisions
// with other Auth.js apps on the same host. See https://github.com/homarr-labs/homarr/issues/5773
export const sessionTokenCookieName = `${env.AUTH_COOKIE_PREFIX}.session-token`;
// Default of authjs
export const sessionTokenCookieName = "authjs.session-token";
export const expireDateAfter = (seconds: number) => {
return new Date(Date.now() + seconds * 1000);
-2
View File
@@ -20,7 +20,6 @@ interface CreateCronJobOptions {
runOnStart?: boolean;
preventManualExecution?: boolean;
preventCustomInterval?: boolean;
hidden?: boolean;
expectedMaximumDurationInMillis?: number;
beforeStart?: () => MaybePromise<void>;
}
@@ -122,7 +121,6 @@ const createCallback = <TAllowedNames extends string, TName extends TAllowedName
},
preventManualExecution: options.preventManualExecution ?? false,
preventCustomInterval: options.preventCustomInterval ?? false,
hidden: options.hidden ?? false,
timezone: creatorOptions.timezone,
};
};
+10 -12
View File
@@ -122,17 +122,15 @@ export class JobManager {
> {
const configurations = await this.db.query.cronJobConfigurations.findMany();
return [...this.jobGroup.getJobRegistry().entries()]
.filter(([_, job]) => !job.hidden)
.map(([name, job]) => {
const config = configurations.find((config) => config.name === name);
return {
name,
cron: config?.cronExpression ?? job.cronExpression,
preventManualExecution: job.preventManualExecution,
preventCustomInterval: job.preventCustomInterval,
isEnabled: config?.isEnabled ?? true,
};
});
return [...this.jobGroup.getJobRegistry().entries()].map(([name, job]) => {
const config = configurations.find((config) => config.name === name);
return {
name,
cron: config?.cronExpression ?? job.cronExpression,
preventManualExecution: job.preventManualExecution,
preventCustomInterval: job.preventCustomInterval,
isEnabled: config?.isEnabled ?? true,
};
});
}
}
+8 -1
View File
@@ -1,6 +1,8 @@
import { sendServerAnalyticsAsync } from "@homarr/analytics";
import { env } from "@homarr/common/env";
import { EVERY_WEEK } from "@homarr/cron-jobs-core/expressions";
import { db } from "@homarr/db";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import { createCronJob } from "../lib";
@@ -8,8 +10,13 @@ export const analyticsJob = createCronJob("analytics", EVERY_WEEK, {
runOnStart: true,
preventManualExecution: true,
preventCustomInterval: true,
hidden: true,
}).withCallback(async () => {
if (env.NO_EXTERNAL_CONNECTION) return;
const analyticSetting = await getServerSettingByKeyAsync(db, "analytics");
if (!analyticSetting.enableGeneral) {
return;
}
await sendServerAnalyticsAsync();
});
+14 -10
View File
@@ -647,13 +647,9 @@ const seedDemoUserAsync = async (db: Database) => {
const seedDefaultCustomWidgetsAsync = async (db: Database) => {
const seedIds = CUSTOM_WIDGET_SEEDS.map((s) => s.id);
const existing = await db.query.customWidgetDefinitions.findMany({
columns: { id: true },
where: inArray(customWidgetDefinitions.id, seedIds),
});
const existingIds = new Set(existing.map((row) => row.id));
const beforeCount = await db.$count(customWidgetDefinitions, inArray(customWidgetDefinitions.id, seedIds));
const seedValues = CUSTOM_WIDGET_SEEDS.filter((seed) => !existingIds.has(seed.id)).map((seed) => {
const seedValues = CUSTOM_WIDGET_SEEDS.map((seed) => {
const parsed = customWidgetImportSchema.parse(seed.data);
return {
id: seed.id,
@@ -672,14 +668,22 @@ const seedDefaultCustomWidgetsAsync = async (db: Database) => {
};
});
if (seedValues.length === 0) {
if (seedValues.length === 0) return;
await db
.insert(customWidgetDefinitions)
.values(seedValues)
.onConflictDoNothing({ target: customWidgetDefinitions.id });
const afterCount = await db.$count(customWidgetDefinitions, inArray(customWidgetDefinitions.id, seedIds));
const createdCount = afterCount - beforeCount;
if (createdCount === 0) {
console.log("Skipping seeding of default custom widgets as they already exist");
return;
}
await db.insert(customWidgetDefinitions).values(seedValues);
console.log(`Created ${seedValues.length} default custom widgets through seeding process`);
console.log(`Created ${createdCount} default custom widgets through seeding process`);
};
const seedBoardWidgetsAsync = async (db: Database) => {
@@ -38,7 +38,7 @@ const itemSchema = z.object({
ServerId: z.string(),
Name: z.string(),
Taglines: z.array(z.string()),
Studios: z.array(z.object({ Name: z.string() })).optional(),
Studios: z.array(z.object({ Name: z.string() })),
Overview: z.string().optional(),
PremiereDate: z
.string()
@@ -172,7 +172,7 @@ export class EmbyIntegration extends Integration implements IMediaServerIntegrat
poster: super.externalUrl(`/Items/${item.Id}/Images/Primary?maxHeight=492&maxWidth=328&quality=90`).toString(),
backdrop: super.externalUrl(`/Items/${item.Id}/Images/Backdrop/0?maxWidth=960&quality=70`).toString(),
},
producer: item.Studios?.at(0)?.Name,
producer: item.Studios.at(0)?.Name,
rating: item.CommunityRating?.toFixed(1),
tags: item.Genres,
href: super.externalUrl(`/web/index.html#!/item?id=${item.Id}&serverId=${item.ServerId}`).toString(),
-1
View File
@@ -15,7 +15,6 @@ export {
createGetSetChannel,
createQueryCacheChannel,
getAllQueryCacheAsync,
invalidateIntegrationCacheAsync,
} from "./lib/channel";
export { createIntegrationHistoryChannel } from "./lib/channels/history-channel";
-30
View File
@@ -261,36 +261,6 @@ export const createIntegrationOptionsChannel = <TData>(
return createChannelWithLatestAndEvents<TData>(channelName);
};
/**
* Invalidates all cached data for a given integration by deleting every Redis key
* that starts with `integration:${integrationId}:` as well as the integration's
* session store entry. This ensures that stale data from a misconfigured or
* updated integration is never served from cache.
*/
export const invalidateIntegrationCacheAsync = async (integrationId: string): Promise<void> => {
const client = getSetClient as typeof getSetClient | null;
if (!client) {
return;
}
const patterns = [`integration:${integrationId}:*`, `session-store:${integrationId}`];
let deletedCount = 0;
for (const pattern of patterns) {
let cursor = "0";
do {
const [nextCursor, keys] = await client.scan(cursor, "MATCH", pattern, "COUNT", 200);
cursor = nextCursor;
if (keys.length > 0) {
await client.del(...keys);
deletedCount += keys.length;
}
} while (cursor !== "0");
}
if (deletedCount > 0) {
logger.info("Invalidated integration cache", { integrationId, deletedKeys: deletedCount });
}
};
export const createWidgetOptionsChannel = <TData>(
widgetKind: WidgetKind,
queryKey: string,
+3 -1
View File
@@ -16,7 +16,9 @@ export type ServerSettingsRecord = Record<(typeof defaultServerSettingsKeys)[num
export const defaultServerSettings = {
analytics: {
enableGeneral: true,
instanceId: null as string | null,
enableWidgetData: false,
enableIntegrationData: false,
enableUserData: false,
},
crawlingAndIndexing: {
noIndex: true,
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "应用",
"description": "将应用嵌入到面板中。",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole 摘要",
"description": "显示 DNS Hole 的摘要",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "crwdns5641:0crwdne5641:0",
"loading": "crwdns5643:0crwdne5643:0",
"placeholder": "crwdns5645:0crwdne5645:0",
"noResults": "crwdns5647:0crwdne5647:0",
"loadError": "crwdns5649:0crwdne5649:0"
},
"app": {
"name": "crwdns732:0crwdne732:0",
"description": "crwdns734:0crwdne734:0",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "crwdns5635:0crwdne5635:0",
"description": "crwdns5637:0crwdne5637:0",
"option": {
"showBroadcastMessage": {
"label": "crwdns5639:0crwdne5639:0"
}
}
},
"dnsHoleSummary": {
"name": "crwdns768:0crwdne768:0",
"description": "crwdns770:0crwdne770:0",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "crwdns4811:0crwdne4811:0",
"error": {
"internalServerError": "crwdns5651:0crwdne5651:0"
},
"column": {
"system": "crwdns4813:0crwdne4813:0",
"cpu": "crwdns4815:0crwdne4815:0",
@@ -3636,9 +3617,6 @@
"label": "crwdns4863:0crwdne4863:0"
}
},
"error": {
"internalServerError": "crwdns5653:0crwdne5653:0"
},
"metric": {
"cpu": "crwdns4865:0crwdne4865:0",
"memory": "crwdns4867:0crwdne4867:0",
@@ -3665,9 +3643,6 @@
}
},
"empty": "crwdns4895:0crwdne4895:0",
"error": {
"internalServerError": "crwdns5655:0crwdne5655:0"
},
"history": "crwdns4897:0crwdne4897:0",
"resolved": "crwdns4899:0crwdne4899:0",
"status": {
@@ -3679,10 +3654,6 @@
"name": "crwdns4905:0crwdne4905:0",
"description": "crwdns4907:0crwdne4907:0",
"option": {
"systemId": {
"label": "crwdns5657:0crwdne5657:0",
"description": "crwdns5659:0crwdne5659:0"
},
"timePeriod": {
"label": "crwdns4909:0crwdne4909:0"
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "crwdns4927:0crwdne4927:0",
"error": {
"internalServerError": "crwdns5661:0crwdne5661:0",
"liveConnectionFailed": "crwdns5663:0crwdne5663:0",
"switchToHistorical": "crwdns5665:0crwdne5665:0",
"systemNotFound": "crwdns5667:0crwdne5667:0"
},
"chart": {
"cpu": {
"title": "crwdns4929:0crwdne4929:0",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "Zobrazí shrnutí vaší DNS Hole",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "App",
"description": "Indlejrer en app i tavlen.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole Oversigt",
"description": "Viser oversigten over dit DNS-hul",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "Bettet eine App in das Board ein.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole Zusammenfassung",
"description": "Zeigt die Zusammenfassung deines DNS Holes an",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
+125 -160
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "Verbinde zuerst eine Integration",
"loading": "Wird geladen...",
"placeholder": "Wählen Sie eine Option",
"noResults": "Keine Ergebnisse gefunden",
"loadError": "Fehler beim Laden der Optionen"
},
"app": {
"name": "App",
"description": "Bettet eine App in das Board ein.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "ArchiveTeam Warrior",
"description": "ArchiveTeam Warrior Status, Projekte und aktive Archivaufträge überwachen.",
"option": {
"showBroadcastMessage": {
"label": "Broadcast Nachrichten anzeigen"
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole Zusammenfassung",
"description": "Zeigt die Zusammenfassung deines DNS Holes an",
@@ -3339,58 +3323,58 @@
},
"uptimeKuma": {
"name": "Uptime Kuma",
"description": "Laufzeit Statistiken von der Uptime Kuma Statusseite überwachen",
"description": "",
"option": {
"showAverageUptime": {
"label": "Durchschnittliche Laufzeit anzeigen",
"description": "Zeige den durchschnittlichen Prozentsatz der 24 Stunden Laufzeit an"
"label": "",
"description": ""
},
"showUptimeRing": {
"label": "Uptime Ring anzeigen",
"description": "Einen Fortschrittsring neben dem durchschnittlichen Laufzeit Wert anzeigen"
"label": "",
"description": ""
},
"showTotalMonitors": {
"label": "Alle Monitore anzeigen",
"description": "Zeigt die Gesamtzahl der konfigurierten Monitore an"
"label": "",
"description": ""
},
"showUpCount": {
"label": "Aktive anzeigen",
"description": "Zeigt an, wie viele Monitore derzeit aktiv sind"
"label": "",
"description": ""
},
"showDownCount": {
"label": "Inaktive anzeigen",
"description": "Zeigt an, wie viele Monitore derzeit nicht verfügbar sind"
"label": "",
"description": ""
},
"showPausedCount": {
"label": "Pausierte anzeigen",
"description": "Zeigt an, wie viele Monitore derzeit pausiert sind"
"label": "",
"description": ""
}
},
"averageUptime": "Durchschnittliche Laufzeit (24h)",
"averageUptime": "",
"stats": {
"totalMonitors": "Alle Monitore",
"upCount": "Aktiv",
"downCount": "Inaktiv",
"pausedCount": "Pausiert"
"totalMonitors": "",
"upCount": "",
"downCount": "",
"pausedCount": ""
},
"error": {
"internalServerError": ""
}
},
"audioStats": {
"name": "Audiostatistiken",
"name": "",
"description": "",
"option": {
"showArtists": {
"label": "Künstler anzeigen",
"label": "",
"description": ""
},
"showAlbums": {
"label": "",
"description": "Zeigt die Gesamtzahl der Alben in deiner Musikbibliothek an (Navidrome)"
"description": ""
},
"showSongs": {
"label": "Songs anzeigen",
"label": "",
"description": ""
},
"showNowPlaying": {
@@ -3419,10 +3403,10 @@
},
"showListeningTime": {
"label": "",
"description": "Zeigt deine gesamte Hörzeit (Audiobookshelf)"
"description": ""
},
"showActiveSessions": {
"label": "Aktive Streams anzeigen",
"label": "",
"description": ""
},
"compactMode": {
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3606,13 +3587,13 @@
"label": ""
},
"showMemory": {
"label": "Arbeitsspeicher anzeigen"
"label": ""
},
"showDisk": {
"label": ""
},
"showGpu": {
"label": "GPU anzeigen"
"label": ""
},
"showLoadAvg": {
"label": ""
@@ -3621,13 +3602,13 @@
"label": ""
},
"showTemp": {
"label": "Temperatur anzeigen"
"label": ""
},
"showBattery": {
"label": ""
},
"showServices": {
"label": "Dienste anzeigen"
"label": ""
},
"showUptime": {
"label": ""
@@ -3636,21 +3617,18 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "CPU",
"memory": "Arbeitsspeicher",
"cpu": "",
"memory": "",
"disk": "",
"gpu": "GPU",
"gpu": "",
"loadAvg": "",
"net": "",
"temp": "",
"battery": "",
"services": "Dienste",
"services": "",
"uptime": "",
"agent": "Agent"
"agent": ""
}
},
"beszelAlerts": {
@@ -3665,24 +3643,17 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "Verlauf",
"history": "",
"resolved": "",
"status": {
"triggered": "",
"ok": "Ok"
"ok": ""
}
},
"beszelSystemStats": {
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3711,27 +3682,21 @@
"label": ""
}
},
"selectSystem": "System auswählen",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"selectSystem": "",
"chart": {
"cpu": {
"title": "CPU-Auslastung",
"title": "",
"subtitle": "",
"series": "CPU"
"series": ""
},
"memory": {
"title": "",
"subtitle": "",
"series": "Belegt",
"series": "",
"cache": ""
},
"disk": {
"title": "Festplattenbelegung",
"title": "",
"subtitle": "",
"series": ""
},
@@ -4282,12 +4247,12 @@
"message": ""
},
"status": {
"completed": "Abgeschlossen",
"notStarted": "Nicht gestartet"
"completed": "",
"notStarted": ""
},
"name": {
"management": "",
"dashboard": "Dashboard"
"dashboard": ""
}
}
}
@@ -4704,15 +4669,15 @@
"subtitle": "",
"description": "",
"noApiKey": "",
"createApiKeyButton": "API Key erstellen",
"createApiKeyButton": "",
"endpoint": {
"title": ""
},
"authentication": {
"title": "Authentifizierung",
"title": "",
"intro": "",
"apiKey": {
"title": "API Key (manuell)",
"title": "",
"description": ""
},
"oauth": {
@@ -4725,12 +4690,12 @@
"clientConfig": {
"title": "",
"streamableHttp": {
"recommended": "Empfohlen",
"recommended": "",
"title": "",
"worksWith": "",
"addToConfig": "",
"cursor": "",
"claudeDesktop": "{name}: {file}",
"claudeDesktop": "",
"claudeCode": "",
"vsCodeCopilot": "",
"replaceHint": ""
@@ -4741,16 +4706,16 @@
}
},
"testConnection": {
"title": "Verbindung testen",
"title": "",
"description": "",
"hint": ""
},
"availableTools": {
"title": "Verfügbare Tools",
"title": "",
"description": "",
"columnType": "Typ",
"columnName": "Tool",
"columnDescription": "Beschreibung"
"columnType": "",
"columnName": "",
"columnDescription": ""
},
"troubleshooting": {
"title": "",
@@ -4779,65 +4744,65 @@
"oneClick": {
"title": "",
"subtitle": "",
"vsCode": "In VS Code installieren",
"cursor": "In Cursor installieren",
"copyUrl": "MCP URL kopieren",
"copied": "Kopiert!",
"copyHint": "Für ChatGPT, Claude Code oder weitere MCP-Client",
"manualConfig": "Manuelle Konfiguration"
"vsCode": "",
"cursor": "",
"copyUrl": "",
"copied": "",
"copyHint": "",
"manualConfig": ""
}
}
}
},
"backup": {
"title": "Komplette Datenbanksicherung",
"metaTitle": "Sichern & Wiederherstellen",
"title": "",
"metaTitle": "",
"export": {
"title": "Datenbank exportieren",
"description": "Laden Sie eine vollständige Sicherung Ihrer SQLite-Datenbank herunter, inklusive aller Einstellungen, Benutzer, Boards und verschlüsselten Integrationsgeheimnissen. Das Backup wird als ZIP-Datei mit dem Verschlüsselungsschlüssel und den Metadaten gebündelt.",
"button": "Sicherung herunterladen",
"error": "Export fehlgeschlagen",
"title": "",
"description": "",
"button": "",
"error": "",
"confirm": {
"title": "",
"warningTitle": "Dieser Export enthält Ihre gesamte Datenbank.",
"warningBody": "Das Backup umfasst alle Benutzer, Integrationsgeheimnisse (verschlüsselt), API-Keys, Board-Konfigurationen und App-Daten. Speichern Sie die exportierte Datei sicher ab und teilen Sie diese nicht.",
"typePrompt": "Geben Sie \"{phrase}\" ein, um fortzufahren",
"submit": "Datenbank exportieren",
"cancel": "Abbrechen"
"warningTitle": "",
"warningBody": "",
"typePrompt": "",
"submit": "",
"cancel": ""
}
},
"import": {
"title": "Datenbank wiederherstellen",
"title": "",
"description": "",
"warning": "",
"dropzone": {
"title": "Ziehen Sie Ihre Backup-ZIP hierher oder klicken Sie zum Durchsuchen",
"description": "Nur .zip Dateien aus einer Homarr SQLite-Sicherung werden akzeptiert"
"title": "",
"description": ""
},
"changeFile": "Datei ändern",
"button": "Datenbank wiederherstellen",
"changeFile": "",
"button": "",
"success": {
"title": "Wiederherstellung abgeschlossen",
"message": "Datenbank wurde erfolgreich wiederhergestellt. Die Anwendung wird neu gestartet..."
"title": "",
"message": ""
},
"error": "Importieren fehlgeschlagen"
"error": ""
},
"preview": {
"title": "",
"exported": "",
"loading": "Analysiere Backup-Datei...",
"loading": "",
"errorTitle": "",
"entity": "",
"count": "",
"boards": "Boards",
"boards": "",
"entities": {
"board": "Boards",
"user": "Benutzer",
"app": "Apps",
"integration": "Integrationen",
"item": "Widgets",
"media": "Medien",
"search_engine": "Suchmaschinen"
"board": "",
"user": "",
"app": "",
"integration": "",
"item": "",
"media": "",
"search_engine": ""
},
"analyzeError": "",
"invalidMissingMetadata": "",
@@ -4845,11 +4810,11 @@
},
"restore": {
"dropzone": {
"title": "Backup-ZIP hierher ziehen",
"description": "Akzeptiert .zip-Dateien von einem Homarr SQLite-Backup (max. 256 MB)"
"title": "",
"description": ""
},
"analyzing": "Analysiere Backup-Datei...",
"changeFile": "Datei ändern",
"analyzing": "",
"changeFile": "",
"continueToRestore": "",
"tryAgain": "",
"analysisError": "",
@@ -4901,7 +4866,7 @@
"message": ""
},
"failed": {
"title": "Wiederherstellung fehlgeschlagen"
"title": ""
},
"invalidMissingMetadata": "",
"invalidMissingDb": "",
@@ -4987,15 +4952,15 @@
"label": "Ports"
},
"host": {
"label": "Host"
"label": ""
}
},
"action": {
"title": "Aktionen",
"logs": {
"label": "Logs anzeigen",
"label": "",
"page": {
"title": "{name} Logs"
"title": ""
}
},
"start": {
@@ -5390,12 +5355,12 @@
}
},
"custom-widgets": {
"label": "Benutzerdefinierte Widgets",
"label": "",
"new": {
"label": "Neu"
"label": ""
},
"edit": {
"label": "Bearbeiten",
"label": "",
"flow": {
"label": ""
}
@@ -5424,7 +5389,7 @@
"docker": {
"label": "Docker",
"logs": {
"label": "Logs"
"label": ""
}
},
"kubernetes": {
@@ -5576,26 +5541,26 @@
}
},
"preferences": {
"title": "Einstellungen",
"title": "",
"children": {
"detail": {
"title": "",
"backHint": "",
"backAction": "Zurück"
"backAction": ""
}
},
"notification": {
"error": {
"message": "Einstellung konnte nicht aktualisiert werden"
"message": ""
}
},
"option": {
"preferences": {
"label": "Einstellungen öffnen"
"label": ""
},
"boolean": {
"enabled": "Aktiviert",
"disabled": "Deaktiviert"
"enabled": "",
"disabled": ""
},
"board": {
"none": "",
@@ -5606,34 +5571,34 @@
"unavailable": ""
},
"colorScheme": {
"label": "Farbschema",
"label": "",
"children": {
"detail": {
"title": "Farbschema auswählen"
"title": ""
}
}
},
"locale": {
"label": "Sprache",
"label": "",
"children": {
"detail": {
"title": "Bevorzugte Sprache auswählen"
"title": ""
}
}
},
"defaultSearchEngineId": {
"label": "Standard Suchmaschine",
"label": "",
"children": {
"detail": {
"title": "Standard Suchmaschine auswählen"
"title": ""
}
}
},
"openSearchInNewTab": {
"label": "Suchergebnisse in neuem Tab öffnen",
"label": "",
"children": {
"detail": {
"title": "Suchergebnisse in neuem Tab öffnen"
"title": ""
}
}
},
@@ -5646,10 +5611,10 @@
}
},
"firstDayOfWeek": {
"label": "Erster Tag der Woche",
"label": "",
"children": {
"detail": {
"title": "Ersten Tag der Woche auswählen"
"title": ""
}
}
},
@@ -5678,23 +5643,23 @@
}
},
"fullPreferencesPage": {
"label": "Alle Einstellungen öffnen"
"label": ""
}
}
}
}
},
"media": {
"help": "Suche nach Filmen oder Serien zum Abfragen",
"help": "",
"requestMovie": "Einen Film anfordern",
"requestSeries": "Eine Serie anfordern",
"openIn": "In {kind} öffnen",
"group": {
"title": "Filme und Serien"
"title": ""
},
"action": {
"search": {
"label": "Suche nach Filmen oder Serien",
"label": "",
"description": "",
"disabled": {
"noIntegration": "",
@@ -6124,23 +6089,23 @@
"description": ""
},
"appsCreate": {
"title": "App hinzufügen",
"title": "",
"description": ""
},
"integrationsList": {
"title": "Ihre Integrationen",
"title": "",
"description": ""
},
"integrationsCreate": {
"title": "Integration hinzufügen",
"title": "",
"description": ""
},
"usersList": {
"title": "Ihre Benutzer",
"title": "",
"description": ""
},
"usersCreate": {
"title": "Benutzer erstellen",
"title": "",
"description": ""
}
},
@@ -6150,7 +6115,7 @@
"description": ""
},
"editMode": {
"title": "Bearbeitungsmodus",
"title": "",
"description": ""
},
"settings": {
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Εφαρμογή",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
+39 -74
View File
@@ -8,9 +8,9 @@
"action": {
"scratch": "Start from scratch",
"importOldmarr": "Import from a Homarr version before v1.0",
"restoreSqlite": "Restore from SQLite backup",
"restoreSqliteDescription": "Upload a full SQLite database backup from a previous Homarr instance. This will restore all boards, users, apps, integrations, and settings.",
"backToOptions": "← Back to setup options"
"restoreSqlite": "",
"restoreSqliteDescription": "",
"backToOptions": ""
}
},
"import": {
@@ -115,24 +115,24 @@
}
},
"urlTemplate": {
"label": "Base URL",
"placeholder": "homelab.local or 192.168.1.1",
"hint": "Enter your base domain or IP to auto-fill integration URLs",
"warning": "Setting your base URL now will auto-fill all integration URLs, saving you time during setup",
"preview": "e.g. {example}",
"label": "",
"placeholder": "",
"hint": "",
"warning": "",
"preview": "",
"mode": {
"subdomain": "Subdomain",
"hostPort": "Host:Port"
"subdomain": "",
"hostPort": ""
}
},
"docker": {
"detected": "Docker detected {count} services",
"detectedBreakdown": "{integrations} integrations · {apps} apps",
"summary": "Homarr detected {apps} apps, {integrations} of which have integrations built-in",
"appBadge": "App",
"appSectionLabel": "Apps to add to dashboard ({count} selected)",
"scanning": "Scanning Docker...",
"scanningDescription": "Looking for running containers on your system"
"detected": "",
"detectedBreakdown": "",
"summary": "",
"appBadge": "",
"appSectionLabel": "",
"scanning": "",
"scanningDescription": ""
}
},
"finish": {
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "App",
"description": "Embeds an app into the board.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole Summary",
"description": "Displays the summary of your DNS Hole",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
@@ -5739,10 +5704,10 @@
"option": {
"google": {
"name": "",
"description": "Search the web with Google"
"description": ""
},
"bing": {
"name": "Bing",
"name": "",
"description": "Search the web with Bing"
},
"duckduckgo": {
@@ -6124,46 +6089,46 @@
"description": "These are all the apps you've added — links to your self-hosted services. Each app has an icon, optional health check, and a status badge on the dashboard."
},
"appsCreate": {
"title": "Add an App",
"description": "Register a new service here. Give it a name, URL, and icon — it will appear as a tile on your boards with live status monitoring."
"title": "",
"description": ""
},
"integrationsList": {
"title": "Your Integrations",
"description": "These are the services connected to Homarr — Sonarr, Radarr, Plex, Jellyfin, and 50+ more. All credentials are encrypted at rest."
"title": "",
"description": ""
},
"integrationsCreate": {
"title": "Add an Integration",
"description": "Connect a new service here. Pick the type, enter the URL and API key — Homarr validates the connection before saving."
"title": "",
"description": ""
},
"usersList": {
"title": "Your Users",
"description": "See everyone who has access. Organise users into groups and control who can view or edit each board."
"title": "",
"description": ""
},
"usersCreate": {
"title": "Create a User",
"description": "Add a new user account here. You can also use invite links for easy onboarding. That's the tour — you're all set!"
"title": "",
"description": ""
}
},
"board": {
"welcome": {
"title": "This is your board",
"description": "A drag-and-drop dashboard where you arrange apps and widgets. Press Ctrl+K anytime to search for apps, boards, and actions."
"title": "",
"description": ""
},
"editMode": {
"title": "Edit Mode",
"description": "Click here to enter edit mode. You can then add widgets, apps, and sections — drag to reposition, resize with the corner handle. Click again to save your changes."
"title": "",
"description": ""
},
"settings": {
"title": "Board Settings",
"description": "Change this board's name, columns, background, colours, and who can access it. Each board can be public or private."
"title": "",
"description": ""
},
"switcher": {
"title": "Switch Boards",
"description": "Jump between boards. Create separate dashboards for different purposes — media, networking, per-user, or anything you like."
"title": "",
"description": ""
},
"userMenu": {
"title": "Your Profile",
"description": "Change your colour scheme, language, home board, and preferences. You can also jump to the management area from here."
"title": "",
"description": ""
}
}
}
+15 -5
View File
@@ -4413,8 +4413,20 @@
"analytics": {
"title": "Analytics",
"general": {
"title": "Help us improve Homarr",
"text": "This helps us figure out what people actually use so we know where to spend our time. We see stuff like how many widgets or integrations you have, your Homarr version, and what database you're on. That's it — no personal info, no URLs, no names, nothing that could identify you or your setup."
"title": "Send anonymous analytics",
"text": "Homarr will send anonymized analytics using the open source software Umami. When enabled, we collect: Homarr version, database type (MySQL/PostgreSQL/SQLite), Docker/Kubernetes status, authentication methods enabled, and the number of each feature. All data is anonymous and aggregated - no personal information, URLs, names, or identifiable data is ever collected. This helps our open source team understand usage patterns and prioritize development."
},
"widgetData": {
"title": "Widget data",
"text": "Send which widgets (and their quantity) you have configured. Does not include URLs, names or any other data."
},
"integrationData": {
"title": "Integration data",
"text": "Send which integrations (and their quantity) you have configured. Does not include URLs, names or any other data."
},
"usersData": {
"title": "Users data",
"text": "Send the amount of users and whether you've activated SSO"
}
},
"crawlingAndIndexing": {
@@ -4942,9 +4954,7 @@
"updated": "Updated {when}",
"search": "Seach {count} containers",
"selected": "{selectCount} of {totalCount} containers selected",
"footer": "Total {count} containers",
"totalCpu": "Total CPU: {cpu}%",
"totalMemory": "Total Mem: {memory}"
"footer": "Total {count} containers"
},
"field": {
"name": {
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Aplicación",
"description": "Insertar una aplicación en el tablero.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "Resumen del agujero DNS",
"description": "Muestra el resumen de tu agujero DNS",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Sovellus",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "App",
"description": "Intègre une application dans le tableau.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "Résumé du puit DNS",
"description": "Affiche le résumé de votre puit DNS",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "אפליקציה",
"description": "הטמעת אפליקציה בלוח.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "סיכום חור אבטחה DNS",
"description": "מציג את הסיכום חור אבטחת DNS שלך",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Alkalmazás",
"description": "Alkalmazást ágyaz be a táblába.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Blokkoló Összefoglaló",
"description": "Megjeleníti a DNS Blokkoló összefoglalóját",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "Connetti prima un'integrazione",
"loading": "Caricamento...",
"placeholder": "Seleziona un'opzione",
"noResults": "Nessun risultato trovato",
"loadError": "Impossibile caricare le opzioni"
},
"app": {
"name": "App",
"description": "Incorpora un'app nella board.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "ArchiveTeam Warrior",
"description": "Monitora lo stato di ArchiveTeam Warrior e i lavori di archiviazione attivi.",
"option": {
"showBroadcastMessage": {
"label": "Mostra messaggi broadcast"
}
}
},
"dnsHoleSummary": {
"name": "Riepilogo del filtraggio DNS",
"description": "Mostra il riepilogo del tuo filtro DNS",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "Nessun sistema trovato",
"error": {
"internalServerError": "Impossibile connettersi a Beszel. Controlla le tue impostazioni di integrazione."
},
"column": {
"system": "Sistema",
"cpu": "CPU",
@@ -3636,9 +3617,6 @@
"label": "Mostra versione agente"
}
},
"error": {
"internalServerError": "Impossibile connettersi a Beszel. Controlla le tue impostazioni di integrazione."
},
"metric": {
"cpu": "CPU",
"memory": "Memoria",
@@ -3665,9 +3643,6 @@
}
},
"empty": "Nessun avviso configurato",
"error": {
"internalServerError": "Impossibile connettersi a Beszel. Controlla le tue impostazioni di integrazione."
},
"history": "Cronologia",
"resolved": "Risolto",
"status": {
@@ -3679,10 +3654,6 @@
"name": "Statistiche Sistema Beszel",
"description": "Grafici a serie temporali per CPU, memoria, disco, rete e metriche dei container Docker",
"option": {
"systemId": {
"label": "Sistema",
"description": "Seleziona un sistema Beszel per visualizzare le statistiche per"
},
"timePeriod": {
"label": "Periodo di tempo"
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "Seleziona sistema",
"error": {
"internalServerError": "Impossibile connettersi a Beszel. Controlla le tue impostazioni di integrazione.",
"liveConnectionFailed": "Connessione Live non riuscita. L'istanza di Beszel potrebbe non supportare lo streaming in tempo reale.",
"switchToHistorical": "Passa alla visualizzazione storica",
"systemNotFound": "Il sistema selezionato non è stato trovato. Scegli un sistema diverso."
},
"chart": {
"cpu": {
"title": "Utilizzo CPU",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "アプリ",
"description": "アプリをボードに埋め込みます。",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole 概要",
"description": "DNS Hole の概要を表示します",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "App",
"description": "Een app insluiten op het bord.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS-hole overzicht",
"description": "Toont het overzicht van je DNS-hole",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "App",
"description": "Bygger inn en app i tavlen.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole Sammendrag",
"description": "Viser sammendrag av DNS Hole",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Aplikacja",
"description": "Dodaje aplikację do tablicy.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "Podsumowanie Hole DNS",
"description": "Wyświetla podsumowanie Twojego hosta DNS",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
+192 -227
View File
@@ -8,9 +8,9 @@
"action": {
"scratch": "Começar do zero",
"importOldmarr": "Importe de uma instância do Homarr anterior a 1.0",
"restoreSqlite": "Restaurar do backup SQLite",
"restoreSqliteDescription": "Fazer o upload de um backup completo do banco de dados SQLite de uma instância anterior da Homarr. Isso restaurará todos os painéis, usuários, apps, integrações e configurações.",
"backToOptions": "← Retorna às opções de instalação"
"restoreSqlite": "",
"restoreSqliteDescription": "",
"backToOptions": ""
}
},
"import": {
@@ -94,45 +94,45 @@
"subtitle": "Definir as configurações do servidor."
},
"integrations": {
"title": "Integrações",
"subtitle": "Selecione os serviços que você usa.",
"description": "Escolha as integrações que deseja conectar. Vamos configurar seu painel com os widgets correspondentes automaticamente.",
"title": "",
"subtitle": "",
"description": "",
"action": {
"continue": "Continuar",
"skip": "Pular por enquanto"
"continue": "",
"skip": ""
},
"configure": {
"progress": "{current} de {total}",
"skip": "Pular esta integração",
"finishing": "Configurando seu painel...",
"progress": "",
"skip": "",
"finishing": "",
"error": {
"title": "Falha na configuração",
"message": "Algo deu errado ao configurar seu painel. Por favor, tente novamente."
"title": "",
"message": ""
},
"helpers": {
"tooltip": "Auxiliares de URL",
"description": "Esses auxiliares tentam adivinhar a URL dos seus serviços. Se não funcionar, digite o endereço manualmente."
"tooltip": "",
"description": ""
}
},
"urlTemplate": {
"label": "URL base",
"placeholder": "homelab.local ou 192.168.1.1",
"hint": "Digite seu domínio base ou IP para preencher automaticamente as URLs das integrações",
"warning": "Definir sua URL base agora preencherá automaticamente todas as URLs das integrações, economizando tempo durante a configuração",
"preview": "ex.: {example}",
"label": "",
"placeholder": "",
"hint": "",
"warning": "",
"preview": "",
"mode": {
"subdomain": "Subdomínio",
"hostPort": "Host:Porta"
"subdomain": "",
"hostPort": ""
}
},
"docker": {
"detected": "Docker detectou {count} serviços",
"detectedBreakdown": "{integrations} integrações · {apps} apps",
"summary": "O Homarr detectou {apps} apps, dos quais {integrations} possuem integrações nativas",
"appBadge": "App",
"appSectionLabel": "Apps para adicionar ao painel ({count} selecionados)",
"scanning": "Verificando Docker...",
"scanningDescription": "Procurando contêineres em execução no seu sistema"
"detected": "",
"detectedBreakdown": "",
"summary": "",
"appBadge": "",
"appSectionLabel": "",
"scanning": "",
"scanningDescription": ""
}
},
"finish": {
@@ -177,7 +177,7 @@
},
"password": {
"label": "Senha",
"suggestionsTitle": "Sugestões de senha",
"suggestionsTitle": "",
"suggestion": {
"length": "Inclua pelo menos 8 caracteres",
"lowercase": "Inclui letras minúsculas",
@@ -687,94 +687,94 @@
"customWidget": {
"page": {
"list": {
"title": "Widgets Personalizados",
"noResults": "Nenhum widget personalizado ainda. Crie o seu primeiro!",
"loading": "Carregando..."
"title": "",
"noResults": "",
"loading": ""
},
"create": {
"title": "Criar widget personalizado",
"subtitle": "Defina uma URL de API, autenticação e como exibir os dados de resposta."
"title": "",
"subtitle": ""
},
"edit": {
"title": "Editar widget personalizado"
"title": ""
}
},
"action": {
"create": "Criar",
"import": "Importar",
"save": "Salvar",
"delete": "Excluir widget personalizado",
"deleteConfirm": "Tem certeza de que deseja excluir \"{name}\"? Esta ação não pode ser desfeita.",
"export": "Exportar",
"addMapping": "Adicionar Mapeamento",
"addColumn": "Adicionar Coluna",
"edit": "Editar",
"removeMapping": "Remover Mapeamento",
"removeColumn": "Remover coluna",
"addItem": "Adicionar Item",
"removeItem": "Remover item",
"addBar": "Adicionar barra",
"duplicate": "Duplicar",
"create": "",
"import": "",
"save": "",
"delete": "",
"deleteConfirm": "",
"export": "",
"addMapping": "",
"addColumn": "",
"edit": "",
"removeMapping": "",
"removeColumn": "",
"addItem": "",
"removeItem": "",
"addBar": "",
"duplicate": "",
"copyAiPrompt": "",
"enable": "Habilitado",
"disable": "Desabilitado",
"menu": "Ações"
"enable": "",
"disable": "",
"menu": ""
},
"field": {
"name": "Nome",
"description": "Descrição",
"iconUrl": "URL do ícone",
"url": "URL",
"authType": "Tipo de Autenticação",
"name": "",
"description": "",
"iconUrl": "",
"url": "",
"authType": "",
"headerName": "",
"method": "Método",
"requestBody": "Corpo da requisição",
"method": "",
"requestBody": "",
"displayType": "",
"jsonPath": "Caminho JSON",
"jsonPathHint": "Usar sintaxe JSONPath para extrair valores, por exemplo, $.data.temperature ou $.items[0].name",
"jsonPath": "",
"jsonPathHint": "",
"label": "",
"unit": "",
"mappings": "Mapeamentos",
"tablePath": "Caminho Array JSON",
"tablePathHint": "Caminho para o array na resposta, por exemplo, $.data.items",
"columns": "Colunas",
"header": "Header (cabeçalho)",
"items": "Itens",
"gridColumns": "Colunas da grade",
"cardStyle": "Estilo de cartão",
"bars": "Barras",
"valuePath": "Caminho do valor JSON",
"maxPath": "Caminho do Max Valor JSON",
"showPercentage": "Mostrar porcentagem",
"barSize": "Tamanho da barra",
"goodValues": "Valores bons (separados por vírgula)",
"mappings": "",
"tablePath": "",
"tablePathHint": "",
"columns": "",
"header": "",
"items": "",
"gridColumns": "",
"cardStyle": "",
"bars": "",
"valuePath": "",
"maxPath": "",
"showPercentage": "",
"barSize": "",
"goodValues": "",
"dotSize": "",
"valueSize": "Tamanho do valor",
"valueSize": "",
"labelPosition": "",
"kvLayout": "Layout",
"striped": "Linhas Listradas",
"compact": "Modo Compacto",
"maxHeight": "Altura máxima (px)",
"color": "Cor",
"kvLayout": "",
"striped": "",
"compact": "",
"maxHeight": "",
"color": "",
"buttonLabel": "",
"buttonColor": "Cor do botão",
"confirmText": "Texto de confirmação",
"confirmTextHint": "Se definido, um diálogo de confirmação irá aparecer antes de executar a solicitação",
"successMessage": "Mensagem de Sucesso",
"successMessageHint": "Mensagem mostrada após execução com sucesso",
"enabled": "Habilitado",
"buttonColor": "",
"confirmText": "",
"confirmTextHint": "",
"successMessage": "",
"successMessageHint": "",
"enabled": "",
"template": {
"label": "Template JSX",
"label": "",
"description": ""
}
},
"placeholder": {
"exampleLabel": "Temperatura da CPU",
"exampleLabel": "",
"exampleLabelShort": "",
"exampleHeader": "Nome",
"exampleHeader": "",
"exampleUnit": "",
"iconUrl": "https://...",
"url": "https://api.exemplo.com/br/api/v1/status",
"iconUrl": "",
"url": "",
"headerName": "",
"jsonPath": "",
"tablePath": "",
@@ -790,35 +790,35 @@
"subtle": ""
},
"layoutOption": {
"list": "Lista",
"grid": "Grade"
"list": "",
"grid": ""
},
"sizeOption": {
"sm": "Pequeno",
"md": "Médio",
"lg": "Grande",
"xl": "Extra grande"
"sm": "",
"md": "",
"lg": "",
"xl": ""
},
"labelPositionOption": {
"above": "Acima",
"below": "Abaixo"
"above": "",
"below": ""
},
"fieldset": {
"general": "Geral",
"connection": "Conexão com API",
"display": "Configurações de tela"
"general": "",
"connection": "",
"display": ""
},
"displayTypeDescription": {
"singleValue": "",
"keyValue": "",
"table": "",
"statGrid": "Estatísticas coloridas em uma grade — como Pi-Hole ou Widgets Rápidos",
"progressBars": "Barras de progresso mostrando uso ou conclusão — para disco, memória, quotas",
"statusIndicator": "Pontos de status coloridos mostrando estados online/offline ou de saúde",
"statGrid": "",
"progressBars": "",
"statusIndicator": "",
"countGrid": "",
"raw": "Saída JSON impressa para depuração",
"actionButton": "Um botão que aciona uma solicitação quando clicado",
"customJsx": "Layout JSX personalizado usando componentes Mantine na lista branca com ligação completa de dados de API"
"raw": "",
"actionButton": "",
"customJsx": ""
},
"method": {
"GET": "",
@@ -848,42 +848,42 @@
},
"secret": {
"apiKey": "",
"username": "Usuário",
"password": "Senha",
"username": "",
"password": "",
"savedPlaceholder": ""
},
"preview": {
"title": "Pré-visualizar",
"title": "",
"test": "",
"hint": "Clique em Testar para visualizar a resposta da API com sua configuração atual.",
"rawResponse": "Resposta JSON Bruta"
"hint": "",
"rawResponse": ""
},
"notification": {
"created": "Widget \"{name}\" criado com sucesso.",
"updated": "Widget \"{name}atualizado com sucesso.",
"deleted": "Widget \"{name}excluído.",
"imported": "Widget importado com sucesso.",
"created": "",
"updated": "",
"deleted": "",
"imported": "",
"aiPromptCopied": "",
"aiPromptDescription": "Copie um prompt pré-construído para colar qualquer chatbot de AI (ChatGPT, Claude, Gemini...). Ele inclui o esquema completo de widget e a sua resposta de API para que a AI possa gerar uma configuração JSON pronta para importar para você.",
"aiPromptCopy": "Copiar para área de transferência",
"aiPromptNoResponse": "Dica: Execute um teste primeiro para incluir a sua API de resposta no prompt para melhores resultados.",
"aiPromptCopyError": "Falha ao copiar prompt para área de transferência. O seu navegador pode não suportar este recurso.",
"createError": "Falha ao criar widget. Verifique se o endpoint API está acessível.",
"updateError": "Falha ao atualizar o widget. Suas alterações não foram salvas.",
"importError": "Falha ao importar o widget. Certifique-se de que o arquivo seja um widget personalizado válido exportado em JSON.",
"exportError": "Falha ao exportar o widget.",
"duplicated": "Widget \"{name}duplicado.",
"duplicateError": "Falha ao duplicar o widget.",
"toggleError": "Falha ao alternar o status do widget.",
"deleteError": "Falha ao excluir o widget.",
"previewError": "Falha ao buscar a pré-visualização. Verifique a URL e tente novamente.",
"executed": "Ação executada com sucesso.",
"executeError": "A ação falhou: {error}"
"aiPromptDescription": "",
"aiPromptCopy": "",
"aiPromptNoResponse": "",
"aiPromptCopyError": "",
"createError": "",
"updateError": "",
"importError": "",
"exportError": "",
"duplicated": "",
"duplicateError": "",
"toggleError": "",
"deleteError": "",
"previewError": "",
"executed": "",
"executeError": ""
},
"table": {
"name": "Nome",
"url": "URL",
"display": "Exibição"
"name": "",
"url": "",
"display": ""
},
"widget": {
"disabled": ""
@@ -983,46 +983,46 @@
"placeholder": "URL de aplicativo personalizada"
},
"apiKeySettings": {
"label": "Obter chave de API"
"label": ""
}
},
"action": {
"create": "Nova integração"
},
"grid": {
"widgetCount": "{count} widgets",
"noWidgets": "Sem widgets",
"detected": "Detectado via Docker"
"widgetCount": "",
"noWidgets": "",
"detected": ""
},
"category": {
"dnsHole": "DNS",
"mediaService": "Mídia",
"calendar": "Calendário",
"mediaSearch": "Busca",
"mediaRelease": "Lançamentos",
"mediaRequest": "Solicitações",
"downloadClient": "Downloads",
"usenet": "Usenet",
"torrent": "Torrent",
"miscellaneous": "Diversos",
"smartHomeServer": "Casa Inteligente",
"indexerManager": "Indexador",
"dnsHole": "",
"mediaService": "",
"calendar": "",
"mediaSearch": "",
"mediaRelease": "",
"mediaRequest": "",
"downloadClient": "",
"usenet": "",
"torrent": "",
"miscellaneous": "",
"smartHomeServer": "",
"indexerManager": "",
"healthMonitoring": "",
"search": "Busca",
"mediaTranscoding": "Transcodificação",
"networkController": "Rede",
"releasesProvider": "Lançamentos",
"notifications": "Notificações",
"search": "",
"mediaTranscoding": "",
"networkController": "",
"releasesProvider": "",
"notifications": "",
"firewall": "Firewall",
"timetable": "",
"photoService": "Fotos",
"documents": "Documentos",
"notes": "Anotações",
"mediaMonitoring": "Monitoramento",
"speedtest": "Teste de Velocidade",
"analytics": "Análises",
"mediaLibrary": "Biblioteca de mídia",
"uptimeMonitoring": "Monitoramento do Uptime"
"photoService": "",
"documents": "",
"notes": "",
"mediaMonitoring": "",
"speedtest": "",
"analytics": "",
"mediaLibrary": "",
"uptimeMonitoring": ""
},
"testConnection": {
"action": {
@@ -1297,7 +1297,7 @@
"newLabel": "Nova chave privada"
},
"slug": {
"label": "Slug página de status",
"label": "",
"newLabel": ""
}
}
@@ -1329,7 +1329,7 @@
},
"paperlessNgx": {
"name": "",
"description": "Sistema de gerenciamento de documentos para arquivamento e organização de documentos digitalizados"
"description": ""
}
},
"media": {
@@ -1384,7 +1384,7 @@
"add": "Adicionar",
"apply": "Aplicar",
"backToOverview": "Voltar à visão geral",
"skip": "Pular",
"skip": "",
"change": "Alterar",
"create": "Criar",
"createAnother": "Criar e recomeçar",
@@ -1621,11 +1621,11 @@
"edit": {
"title": "Editar item",
"tab": {
"widget": "Editar widget",
"app": "Editar aplicativo"
"widget": "",
"app": ""
},
"app": {
"propagationNotice": "As alterações deste aplicativo serão aplicadas em todos os lugares em que for usado em todos os painéis."
"propagationNotice": ""
},
"advancedOptions": {
"label": "Opções avançadas",
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Aplicativo",
"description": "Incorpora um aplicativo no quadro.",
@@ -1738,7 +1731,7 @@
"label": "Abrir em nova guia"
},
"withBorder": {
"label": "Mostrar bordas"
"label": ""
},
"items": {
"label": "Marcadores",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "Monitore o status do ArchiveTeam Warrior, projetos e trabalhos de arquivamento ativos.",
"option": {
"showBroadcastMessage": {
"label": "Mostrar mensagens de transmissão"
}
}
},
"dnsHoleSummary": {
"name": "Resumo do DNS Hole",
"description": "Exibe o resumo do seu DNS Hole",
@@ -2406,31 +2390,31 @@
}
},
"ups": {
"name": "UPS",
"description": "Monitore o status de seus dispositivos UPS através de um servidor NUT",
"name": "",
"description": "",
"option": {
"showBattery": {
"label": "Exibir bateria"
"label": ""
},
"showLoad": {
"label": "Mostrar carga"
"label": ""
},
"showVoltage": {
"label": "Mostrar tensão"
"label": ""
}
},
"status": {
"online": "Online",
"onBattery": "Na bateria",
"lowBattery": "Bateria fraca",
"charging": "Carregando",
"unknown": "Desconhecido"
"online": "",
"onBattery": "",
"lowBattery": "",
"charging": "",
"unknown": ""
},
"field": {
"runtime": "Tempo de execução",
"load": "Carga",
"input": "Entrada",
"output": "Saída"
"runtime": "",
"load": "",
"input": "",
"output": ""
},
"error": {
"internalServerError": ""
@@ -2685,8 +2669,8 @@
}
},
"paperlessNgx": {
"name": "Paperless-ngx",
"description": "Visão geral do seu arquivo de documentos Paperless-ngx",
"name": "",
"description": "",
"documentsTotal": "",
"documentsInbox": "",
"correspondents": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
@@ -4168,7 +4133,7 @@
"items": {
"documentation": "Documentação",
"submitIssue": "Enviar um problema",
"discord": "Discord da comunidade",
"discord": "Discórdia da comunidade",
"sourceCode": "Código-fonte"
}
},
@@ -5776,7 +5741,7 @@
"label": "Reportar um problema"
},
"discord": {
"label": "Discord da comunidade"
"label": "Discórdia da comunidade"
}
}
}
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Приложение",
"description": "Встраивает приложение в панель.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "Сводка DNS-фильтра",
"description": "Отображает сводную информацию о вашем DNS-фильтре",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Aplikácia",
"description": "Vloží aplikáciu do tabule.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "Zhrnutie dier DNS",
"description": "Zobrazí súhrn vašej diery DNS",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Uygulama",
"description": "Panelin içine uygulama yerleştirir.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Çözümleyici Özeti",
"description": "DNS Çözümleyici özetini görüntüler",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
+1 -36
View File
@@ -812,7 +812,7 @@
"singleValue": "",
"keyValue": "",
"table": "",
"statGrid": "Кольорові картки статистики в сітці — як-от віджети Pi-Hole або Speedtest",
"statGrid": "",
"progressBars": "",
"statusIndicator": "",
"countGrid": "",
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "Додаток",
"description": "Вбудовує додаток до дошки.",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "",
"description": "",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "",
"description": "",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
-35
View File
@@ -1652,13 +1652,6 @@
}
},
"widget": {
"integrationSelect": {
"configureIntegrationFirst": "",
"loading": "",
"placeholder": "",
"noResults": "",
"loadError": ""
},
"app": {
"name": "應用程式",
"description": "將應用程式新增至面板中",
@@ -1746,15 +1739,6 @@
}
}
},
"archiveTeamWarrior": {
"name": "",
"description": "",
"option": {
"showBroadcastMessage": {
"label": ""
}
}
},
"dnsHoleSummary": {
"name": "DNS Hole 概況",
"description": "顯示 DNS Hole 的概況",
@@ -3577,9 +3561,6 @@
}
},
"noRecords": "",
"error": {
"internalServerError": ""
},
"column": {
"system": "",
"cpu": "",
@@ -3636,9 +3617,6 @@
"label": ""
}
},
"error": {
"internalServerError": ""
},
"metric": {
"cpu": "",
"memory": "",
@@ -3665,9 +3643,6 @@
}
},
"empty": "",
"error": {
"internalServerError": ""
},
"history": "",
"resolved": "",
"status": {
@@ -3679,10 +3654,6 @@
"name": "",
"description": "",
"option": {
"systemId": {
"label": "",
"description": ""
},
"timePeriod": {
"label": ""
},
@@ -3712,12 +3683,6 @@
}
},
"selectSystem": "",
"error": {
"internalServerError": "",
"liveConnectionFailed": "",
"switchToHistorical": "",
"systemNotFound": ""
},
"chart": {
"cpu": {
"title": "",
+3
View File
@@ -3,6 +3,9 @@ import { z } from "zod/v4";
export const settingsInitSchema = z.object({
analytics: z.object({
enableGeneral: z.boolean(),
enableWidgetData: z.boolean(),
enableIntegrationData: z.boolean(),
enableUserData: z.boolean(),
}),
crawlingAndIndexing: z.object({
noIndex: z.boolean(),
@@ -40,7 +40,7 @@ export default function BeszelAlertsWidget({
() => ({ integrationIds, includeHistory: options.showHistory, maxHistoryItems: options.maxHistoryItems }),
[integrationIds, options.showHistory, options.maxHistoryItems],
);
const { data: results = [], error: alertsError } = clientApi.widget.beszel.getAlerts.useQuery(alertsInput, {
const { data: results = [] } = clientApi.widget.beszel.getAlerts.useQuery(alertsInput, {
staleTime: 15_000,
refetchInterval: 15_000,
retry: false,
@@ -91,8 +91,6 @@ export default function BeszelAlertsWidget({
const triggeredAlerts = alerts.filter((a) => a.triggered);
const okAlerts = alerts.filter((a) => !a.triggered);
if (alertsError) throw alertsError;
return (
<ScrollArea h="100%" style={{ pointerEvents: isEditMode ? "none" : undefined }}>
<Stack gap="sm" p="sm">
@@ -1,7 +1,7 @@
"use client";
import type { MantineSize } from "@mantine/core";
import { Badge, Box, Card, Group, Progress, Text, Stack } from "@mantine/core";
import { Badge, Box, Card, Group, Progress, Text, Stack, useComputedColorScheme } from "@mantine/core";
import {
Activity,
Battery,
@@ -153,6 +153,7 @@ interface SystemCardProps {
t: ReturnType<typeof useScopedI18n<"widget.beszelSystemGrid">>;
size: SizeConfig;
maxMetrics: number;
backgroundColor: string;
itemRadius: MantineSize;
}
@@ -310,14 +311,14 @@ const metricRenderers = [
},
] as const;
const SystemCard = ({ system, options, t, size, maxMetrics, itemRadius }: SystemCardProps) => {
const SystemCard = ({ system, options, t, size, maxMetrics, backgroundColor, itemRadius }: SystemCardProps) => {
const visibleMetrics = metricRenderers.filter((m) => m.visible(system, options)).slice(0, maxMetrics);
return (
<Card
padding={size.cardPadding}
radius={itemRadius}
bg="transparent"
bg={backgroundColor}
h="100%"
style={{
overflow: "hidden",
@@ -353,8 +354,12 @@ export default function BeszelSystemGridWidget({
}: WidgetComponentProps<"beszelSystemGrid">) {
const t = useScopedI18n("widget.beszelSystemGrid");
const board = useRequiredBoard();
const colorScheme = useComputedColorScheme("light");
const opacity = board.opacity / 100;
const bgBase: Record<"light" | "dark", string> = { dark: "57, 57, 57", light: "246, 247, 248" };
const backgroundColor = `rgba(${bgBase[colorScheme]}, ${opacity})`;
const { data: results = [], error: systemsError } = clientApi.widget.beszel.getSystems.useQuery(
const { data: results = [] } = clientApi.widget.beszel.getSystems.useQuery(
{ integrationIds },
{ staleTime: 5_000, refetchInterval: 5_000, retry: false },
);
@@ -362,8 +367,6 @@ export default function BeszelSystemGridWidget({
useBeszelSystemsSubscription(integrationIds, !isEditMode);
const filteredSystems = useBeszelFilteredSystems(results, options.statusFilter);
if (systemsError) throw systemsError;
const cols = getColCount(width, filteredSystems.length);
const rows = Math.ceil(filteredSystems.length / cols) || 1;
const rawCellHeight = height / rows;
@@ -393,6 +396,7 @@ export default function BeszelSystemGridWidget({
t={t}
size={size}
maxMetrics={maxMetrics}
backgroundColor={backgroundColor}
itemRadius={board.itemRadius}
/>
))}
@@ -3,12 +3,4 @@
top: 6px;
left: 6px;
z-index: 1;
opacity: 0;
transition: opacity 0.15s ease;
pointer-events: none;
}
.beszelStatsContainer:hover .beszelStatsSystemToggle {
opacity: 1;
pointer-events: auto;
}
@@ -61,11 +61,10 @@ export default function BeszelSystemStatsWidget({
const { data: session } = useSession();
const hasChangeAccess = board ? constructBoardPermissions(board, session).hasChangeAccess : false;
const { mutate: saveItemOptions } = clientApi.widget.options.saveItemOptions.useMutation();
const {
data: systemsResult = [],
isPending: systemsPending,
error: systemsError,
} = clientApi.widget.beszel.getSystems.useQuery({ integrationIds }, { staleTime: 30 * 1000, retry: false });
const { data: systemsResult = [], isPending: systemsPending } = clientApi.widget.beszel.getSystems.useQuery(
{ integrationIds },
{ staleTime: 30 * 1000 },
);
const systems = useMemo(
() => systemsResult.flatMap((r) => r.systems.map((s) => ({ value: s.id, label: s.name }))),
@@ -90,14 +89,14 @@ export default function BeszelSystemStatsWidget({
const showDocker = options.showDockerCpu || options.showDockerMemory || options.showDockerNetwork;
const isLive = options.timePeriod === "1m";
const { data: statsResult, error: statsError } = clientApi.widget.beszel.getSystemStats.useQuery(
const { data: statsResult } = clientApi.widget.beszel.getSystemStats.useQuery(
{
integrationIds,
systemId: selectedSystem,
timePeriod: options.timePeriod as "1m" | "1h" | "12h" | "24h" | "1w" | "30d",
includeDocker: showDocker,
},
{ staleTime: 30 * 1000, enabled: !isLive && systemReady && selectedSystem !== "", retry: false },
{ staleTime: 30 * 1000, enabled: !isLive && systemReady && selectedSystem !== "" },
);
const { data: liveData, error: liveError } = useLiveStats(integrationIds, selectedSystem, isLive && systemReady);
@@ -186,9 +185,6 @@ export default function BeszelSystemStatsWidget({
const cols = gridColumns[Number(width > 600)];
if (systemsError) throw systemsError;
if (!isLive && statsError) throw statsError;
if (!systemsPending && !systemExists && systems.length > 0) {
return (
<Center h="100%">
@@ -226,11 +222,7 @@ export default function BeszelSystemStatsWidget({
}
return (
<ScrollArea
h="100%"
className={classes.beszelStatsContainer}
style={{ pointerEvents: (isEditMode && "none") || undefined }}
>
<ScrollArea h="100%" style={{ pointerEvents: (isEditMode && "none") || undefined }}>
<Stack gap="md" p="sm">
{!isEditMode && systems.length > 1 && (
<Menu position="bottom-start" withArrow shadow="md" withinPortal>
@@ -66,7 +66,7 @@ export default function BeszelSystemTableWidget({
width,
}: WidgetComponentProps<"beszelSystemTable">) {
const t = useScopedI18n("widget.beszelSystemTable");
const { data: results = [], error: systemsError } = clientApi.widget.beszel.getSystems.useQuery(
const { data: results = [] } = clientApi.widget.beszel.getSystems.useQuery(
{ integrationIds },
{ staleTime: 5_000, refetchInterval: 5_000, retry: false },
);
@@ -255,8 +255,6 @@ export default function BeszelSystemTableWidget({
return cols.filter(Boolean) as DataTableColumn<SystemRowWithKey>[];
}, [options, t, size]);
if (systemsError) throw systemsError;
return (
<DataTable
style={{ pointerEvents: isEditMode ? "none" : undefined }}
@@ -1,17 +1,3 @@
/* Make the DataTable transparent so the board card's opacity-controlled
background shows through. The mantine-datatable defaults to
var(--mantine-color-body) which is opaque. */
.beszel-table,
.beszel-table .mantine-datatable-table,
.beszel-table .mantine-datatable-table thead,
.beszel-table .mantine-datatable-table tbody,
.beszel-table .mantine-datatable-table tfoot,
.beszel-table .mantine-datatable-table tr,
.beszel-table th,
.beszel-table td {
background-color: transparent !important;
}
.beszel-table th {
white-space: nowrap;
}
@@ -19,7 +19,7 @@ function prepareRecords<T>(records: T[], live: boolean) {
}
const yAxisBase = { tickMargin: 0, tick: { fontSize: 10 } } as const;
const chartStyle = { minWidth: 0, minHeight: 1 } as const;
const chartStyle = { minWidth: 0, minHeight: 0 } as const;
const panelStyle = { minWidth: 0, overflow: "hidden" } as const;
export const CPU_Y_AXIS_DOMAIN: [number, string] = [0, "auto"];
+4 -29
View File
@@ -200,17 +200,6 @@ export default function DockerWidget({ options, width, isEditMode }: WidgetCompo
const totalContainers = containers.length;
const totals = useMemo(() => {
return containers.reduce(
(acc, container) => {
acc.cpu += safeValue(container.cpuUsage);
acc.memory += safeValue(container.memoryUsage);
return acc;
},
{ cpu: 0, memory: 0 }
);
}, [containers]);
const columns = useMemo(() => createColumns(t), [t]);
const table = useTranslatedMantineReactTable({
@@ -272,26 +261,12 @@ export default function DockerWidget({ options, width, isEditMode }: WidgetCompo
}}
p={4}
>
<Group gap={4} wrap="nowrap">
<IconBrandDocker size={20} style={{ flexShrink: 0 }} />
<Text size="sm" truncate="end">
{t("table.footer", { count: totalContainers.toString() })}
</Text>
<Group gap={4}>
<IconBrandDocker size={20} />
<Text size="sm">{t("table.footer", { count: totalContainers.toString() })}</Text>
</Group>
<Group gap="sm" wrap="wrap" justify="flex-end" style={{ flex: 1, minWidth: 0 }}>
<Text size="sm" style={{ whiteSpace: "nowrap" }}>
{t("table.totalCpu", { cpu: totals.cpu.toFixed(2) })}
</Text>
<Text size="sm" style={{ whiteSpace: "nowrap" }}>
{t("table.totalMemory", { memory: humanFileSize(totals.memory) })}
</Text>
<Text size="sm" style={{ whiteSpace: "nowrap" }}>
{t("table.updated", { when: relativeTime })}
</Text>
</Group>
<Text size="sm">{t("table.updated", { when: relativeTime })}</Text>
</Group>
)}
</Stack>
@@ -529,16 +529,6 @@ export default function DownloadClientsWidget({
},
{
...columnsDefBase({ key: "time", showHeader: true }),
sortingFn: (rowA, rowB) => {
const timeA = rowA.getValue<ExtendedDownloadClientItem["time"]>("time");
const timeB = rowB.getValue<ExtendedDownloadClientItem["time"]>("time");
// Map time values to canonical sort keys:
// positive = ETA in ms (sort as-is, smaller = sooner)
// 0 = stalled/infinite (sort last in ascending = Infinity)
// negative = time since completion in ms (sort first in ascending = very negative)
const toSortKey = (time: number) => (time === 0 ? Infinity : time);
return toSortKey(timeA) - toSortKey(timeB);
},
Cell: ({ cell }) => {
const time = cell.getValue<ExtendedDownloadClientItem["time"]>();
return time === 0 ? <IconInfinity size={16} /> : <Text size="xs">{dayjs().add(time).fromNow()}</Text>;
+1426 -912
View File
File diff suppressed because it is too large. Load diff
+29 -30
View File
@@ -49,25 +49,25 @@ catalog:
"@tanstack/react-query": ^5.101.0
"@tanstack/react-query-devtools": ^5.101.0
"@tanstack/react-query-next-experimental": ^5.101.0
"@testcontainers/mysql": ^12.0.2
"@testcontainers/postgresql": ^12.0.2
"@testcontainers/redis": ^12.0.2
"@tiptap/extension-color": 3.26.1
"@tiptap/extension-highlight": 3.26.1
"@tiptap/extension-image": 3.26.1
"@tiptap/extension-link": ^3.26.1
"@tiptap/extension-placeholder": ^3.26.1
"@tiptap/extension-table": 3.26.1
"@tiptap/extension-table-cell": 3.26.1
"@tiptap/extension-table-header": 3.26.1
"@tiptap/extension-table-row": 3.26.1
"@tiptap/extension-task-item": 3.26.1
"@tiptap/extension-task-list": 3.26.1
"@tiptap/extension-text-align": 3.26.1
"@tiptap/extension-text-style": 3.26.1
"@tiptap/extension-underline": 3.26.1
"@tiptap/react": ^3.26.1
"@tiptap/starter-kit": ^3.26.1
"@testcontainers/mysql": ^12.0.1
"@testcontainers/postgresql": ^12.0.1
"@testcontainers/redis": ^12.0.1
"@tiptap/extension-color": 3.26.0
"@tiptap/extension-highlight": 3.26.0
"@tiptap/extension-image": 3.26.0
"@tiptap/extension-link": ^3.26.0
"@tiptap/extension-placeholder": ^3.26.0
"@tiptap/extension-table": 3.26.0
"@tiptap/extension-table-cell": 3.26.0
"@tiptap/extension-table-header": 3.26.0
"@tiptap/extension-table-row": 3.26.0
"@tiptap/extension-task-item": 3.26.0
"@tiptap/extension-task-list": 3.26.0
"@tiptap/extension-text-align": 3.26.0
"@tiptap/extension-text-style": 3.26.0
"@tiptap/extension-underline": 3.26.0
"@tiptap/react": ^3.26.0
"@tiptap/starter-kit": ^3.26.0
"@trpc/client": ^11.17.0
"@trpc/next": ^11.17.0
"@trpc/react-query": ^11.17.0
@@ -88,12 +88,11 @@ catalog:
"@types/prismjs": ^1.26.6
"@types/react": 19.2.17
"@types/react-dom": 19.2.3
"@scalar/api-reference-react": ^0.9.46
"@scalar/api-reference-react": ^0.9.45
"@types/video.js": ^7.3.58
"@types/ws": ^8.18.1
"@types/xml2js": ^0.4.14
posthog-js: ^1.255.0
posthog-node: ^4.17.0
"@umami/node": ^0.4.0
"@vitejs/plugin-react": ^6.0.2
"@vitest/coverage-v8": ^4.1.8
"@vitest/ui": ^4.1.8
@@ -131,16 +130,16 @@ catalog:
glob: ^13.0.6
ical.js: ^2.2.1
ioredis: 5.11.1
isomorphic-dompurify: ^3.17.0
jotai: ^2.20.1
isomorphic-dompurify: ^3.16.0
jotai: ^2.20.0
jsdom: ^29.1.1
json5: ^2.2.3
jsonpath-plus: ^10.4.0
ldapts: 8.1.8
lodash.clonedeep: ^4.5.0
lucide-react: ^1.18.0
"mantine-contextmenu": ^9.3.0
mantine-datatable: ^9.3.0
lucide-react: ^1.17.0
"mantine-contextmenu": ^9.2.1
mantine-datatable: ^9.2.2
mantine-react-table: npm:mantine-react-table-open@9.0.3
mcp-handler: ^1.1.0
maria2: ^0.4.1
@@ -164,12 +163,12 @@ catalog:
react-quill-new: ^3.8.3
react-simple-code-editor: ^0.14.1
recharts: ^3.8.1
sass: ^1.101.0
sass: ^1.100.0
semantic-release: ^25.0.5
semver-parser: ^4.2.1
superjson: 2.2.6
svgson: ^5.3.1
testcontainers: ^12.0.2
testcontainers: ^12.0.1
timezones-ical-library: ^2.2.0
trpc-to-mcp: ^1.3.2
trpc-to-openapi: ^3.3.0
@@ -217,7 +216,7 @@ overrides:
"prismjs@<1.30.0": ">=1.30.0"
"proxmox-api>undici": "7.27.1"
"react-is": "^19.2.7"
"rollup@>=4.0.0 <4.22.4": ">=4.62.0"
"rollup@>=4.0.0 <4.22.4": ">=4.61.1"
"sha.js@<=2.4.11": ">=2.4.12"
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2"
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2"
+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 @@
[{"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","imagLine truncated
[{"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","imagLine 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