The widget-only integrations refactor incorrectly removed the
ArchiveTeam Warrior integration entirely. It only needed to be
excluded from auto-seeding (by removing defaultUrl), not deleted.
This restores the integration class, reverts the widget/router/handler
to use integration middleware, and removes archiveTeamWarrior from the
widget-only-integrations migration.
The MantineReactTable defaults table cell backgrounds to
var(--mantine-color-body) which is opaque. This makes the
downloads widget table block appear solid while the footer
and other widgets correctly show the board's background.
Added a styles.css targeting table elements to force
background-color: transparent, following the same pattern
used by the beszel-system-table widget. Also added
bg=transparent to mantinePaperProps for the Paper wrapper.
Adds a Docker button to the user avatar dropdown menu (admin-only)
that opens a 1400px modal containing the same Docker container table
as /manage/tools/docker.
Closes#2165
* fix(manage/about): bypass self-fetch to prevent ECONNREFUSED crash
The /manage/about page fetched its own API routes over HTTP
(http://$HOSTNAME:3000/api/about/...). When the server could not
reach itself (misconfigured HOSTNAME, restricted container networking,
reverse-proxy setups), fetch threw ECONNREFUSED and the whole page
crashed with a full-page Next.js error (issue #6008).
All four routes were force-static serving static JSON / a direct fs
read, so the HTTP round-trip was unnecessary. Replace the fetches
with direct JSON imports and a direct getDependenciesAsync() call,
eliminating the self-network dependency entirely.
Also add a manage-level error.tsx boundary (Next.js 16 convention) so
that any future runtime error in a manage page renders a friendly,
localised fallback inside the existing layout (sidebar stays usable)
instead of the ugly default full-page error.
Exploration draft by an LLM.
Refs #6008
* style: auto-fix lint and format
* refactor: use @static-data path alias, simplify error ternary
- Add @static-data/* tsconfig path alias to replace 7-level relative
imports (../../../../../../../static-data/...) with clean alias
imports across the about page and all 3 API route handlers.
- Simplify error.tsx: replace ternary with && short-circuit.
- Preserve CI lint bot's .sort() -> .toSorted() fix.
Refs #6008
Beszel only stores stats at 1m, 10m, 20m, 120m, 480m intervals.
The 1440m type doesn't exist, so queries for 1w/30d returned empty
results. Use 480m (8h) with appropriate perPage (21 for 1w, 90 for 30d).
* fix(beszel): harden cache layer and prevent blank widgets
- Add debug logging across Beszel integration, request handler, and tRPC
router (auth, fetch, SSE, per-procedure entry/exit) gated behind
LOG_LEVEL=debug
- cached-request-handler: retry with backoff, isValid predicate to skip
caching invalid data, in-flight dedup (single-flight per channel) to
avoid thundering herd after cache invalidation
- redis channel: set-then-publish so storage is always consistent even
when pubsub publish fails
- Beszel handlers: opt into fallbackToStaleOnError and retry so brief
upstream hiccups don't throw away good cached data
- beszel-system-stats: add refetchInterval matching each time period's
cache TTL so historical charts refresh in the background
- tRPC router: switch Promise.all -> Promise.allSettled for getSystems
and getAlerts; getSystemStats returns an error object instead of
throwing. Each result carries an optional error field so one failed
integration no longer nukes the whole widget
- Widgets: add explicit isPending loader, empty state for grid and
stats, and a shared per-integration error indicator (tooltip with
failed integration names). Alerts and table now only show their
empty state after loading completes
- Translation: add empty.noSystems key to beszelSystemGrid and
beszelSystemStats
Addresses #6030
* style: auto-fix lint and format
---------
Co-authored-by: ajnart <ajnart@users.noreply.github.com>
* feat(analytics): migrate from Umami to PostHog server-side analytics
Replace @umami/node with posthog-node for all server-side telemetry.
Add singleton PostHog client, feature tracking POC, DAU heartbeat,
and tRPC analytics router. Cron job is now locked (no manual trigger,
no custom interval). Update consent copy to explain what we collect
and why.
* style: auto-fix lint and format
* refactor(analytics): simplify + add client-side posthog-js init
- Inline constants.ts and track.ts into client.ts (single source)
- Remove dead exports (shutdownPostHogAsync, getPostHogClient)
- Flatten all DB queries into one Promise.all (no waterfall)
- Derive integration/widget totals from group-by (drop redundant counts)
- Add posthog-js with dynamic import, gated on enableGeneral
- Move Analytics component to body, pass consent from server settings
* style: auto-fix lint and format
* feat(cron): add hidden flag to cron jobs, hide analytics from task panel
The analytics job runs silently in the background — users don't need
to see or interact with it. Hidden jobs are filtered out of getAllAsync
so they never appear in the management UI.
* refactor(analytics): simplify to single toggle, always collect all data
Remove granular enableWidgetData/enableIntegrationData/enableUserData
toggles. Analytics is now a single on/off switch. When enabled, all
anonymous usage data is collected in one flat Promise.all call.
Side effect: existing users with enableGeneral=true who had granular
flags off will now send all data. This is by design — the granular
controls added complexity without meaningful privacy benefit since
the data is already anonymous counts.
* style: auto-fix lint and format
* Calculate and display total CPU and memory usage
Added total CPU and memory usage calculations for containers.
* Refactor Docker component layout
Refactor component layout and remove redundant Group elements.
* Create test.yaml
* Add files via upload
* Delete packages/icons/icons directory
Accidental
* Add total CPU and memory strings to English translations
* Add English translations and size refactor
* Fix typo in search label in en.json
* chore(docs): update OpenAPI spec
* Remove color styling from CPU and memory usage text
* Enhance Docker component with footer and toolbar updates
Updated createColumns function to include totals and relativeTime parameters for footer display. Adjusted table configuration to enable bottom toolbar based on isTiny flag.
* Fix formatting and add missing newline at end of file
* Enable sticky header and footer in component
Enable sticky header and add footer row properties for sticky positioning.
* Refactor table component properties
* Refactor table columns and fix footers
* Revert "chore(docs): update OpenAPI spec"
This reverts commit 26f3c809d9.
---------
Co-authored-by: Bergasha <Bergasha@users.noreply.github.com>
Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
Stalled torrents have time=0 (infinite/never), which raw numeric sort
places between negative (completed) and positive (active) values.
Add a custom sortingFn on the time column that maps 0 to Infinity so
stalled entries always sort last in ascending order and first in
descending order.
Closes#5416
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
- Remove ternary operators from new code (widget-integration-select,
TabsContainer, mock data, members page)
- Remove stale /docs/management/tools/backup from sitemap type
- Add value prop to error-state Select in beszel-system-stats
- Add generation guard to prevent late SSE events from writing stale
data after systemId/integration changes
- Sort integrationIds for stable key (avoids unnecessary resubscribes)
- Include integrationKey and systemId in flush interval deps
- Preserve current form value in WidgetIntegrationSelectInput when it's
missing from loaded options (matches WidgetDynamicSelectInput pattern)
react-jsx-parser auto-generates random keys on every render, causing
full remounts that reset uncontrolled Tabs to defaultValue. Fix by:
- Adding disableKeyGeneration to preserve React reconciliation
- Memoizing bindings to avoid unnecessary re-parses
- Converting TabsContainer to controlled Tabs with useState
- Validating defaultTab against actual panel values
- Clearing stale parseErrors when template/data changes
Merging dev introduced the Gotify integration and ArchiveTeam Warrior
widget/integration, which require entries in the doc-slug maps and the
generated documentation sitemap. Also remove a misplaced non-null
assertion in the group members page flagged by oxlint.
- Throw TRPCError instead of returning null when no integration
- Sort beszelRouter import alphabetically in mcp.ts
- Replace ternary with lookup table for background color
- Mention integration_all in MCP server instructions
- Add .default("1h") to timePeriod so MCP clients can omit it
- Include "1m" in the description's valid timePeriod values
- Clarify that getSystemStats uses only the first integrationId
- Use MantineSize type instead of plain string for itemRadius
Register beszelRouter in the MCP server with .meta({ mcp: enabled })
on getSystems, getAlerts, and getSystemStats procedures. AI assistants
can now query Beszel-monitored systems, view alerts, and fetch
historical metrics through the MCP protocol.
Ref #5968
The inner Card components used Mantine's default opaque background and
hard-coded radius, clashing with board styling. Now uses board.opacity
and board.itemRadius with the same pattern as system-resources widgets.
Closes#5968
* feat(integrations): add Gotify notifications integration
Closes#4186
> "It would be really useful if Homarr could integrate with Gotify —
> view notifications from Gotify right inside the Homarr dashboard,
> maybe as a widget or a small panel. A widget that displays the latest
> messages from Gotify (with title, message, time)."
> — #4186: feat: Gotify Integration - display notifications in Homarr
- `gotify/gotify-schema.ts` — Zod schema validating the `GET /message`
API response from the Gotify server, including id, date, title,
message body, and optional priority fields.
- `gotify/gotify-integration.ts` — `GotifyIntegration` class extending
the base `Integration` and implementing `INotificationsIntegration`.
Authenticates via HTTP Basic Auth (username + password) and fetches
the latest 100 messages from the `/message` endpoint. Connection
testing hits `/health`.
- `index.ts` — exports `GotifyIntegration` from the package public API.
- `base/creator.ts` — registers `GotifyIntegration` in the integration
creator map so it can be instantiated at runtime.
- `integration.ts` — registers the `gotify` integration definition with:
- `secretKinds: [["username", "password"]]` (Basic Auth credentials)
- `category: ["notifications"]` (picked up automatically by the
Notifications widget via `getIntegrationKindsByCategory`)
- Icon sourced from `homarr-labs/dashboard-icons`
- Documentation URL pointing to `/docs/integrations/gotify`
- `docs/homarr-docs-sitemap.ts` — adds `/docs/integrations/gotify` to
the `HomarrDocumentationPath` union type so the documentation link
passes the TypeScript path validation check.
Because `gotify` is registered under the `notifications` category, the
existing Notifications widget automatically supports it with no widget
code changes required. Users add the integration via Settings →
Integrations, supply their Gotify server URL, username, and password,
then attach it to a Notifications widget on any board.
* refactor(integrations): inline json parse and add error handling in Gotify integration
Address PR review feedback: remove intermediate `as unknown` cast by inlining
response.json() directly, and use safeParseAsync to handle deserialization
failures gracefully instead of throwing unhandled.
On macOS (and some Glances configurations) the /api/4/all endpoint
omits the quicklook key, causing a Zod invalid_type error and crashing
the system health widget. Mark quicklook as .optional() in allSchema
and add a null-safe fallback (`?? "Unknown"`) for cpuModelName.
Fixes#5728
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>