Commit Graph
363 Commits
Author SHA1 Message Date
Thomas Camlong b3f58cd8aa perf: drop the cache layers that measured at zero
Ablation (PHASE2-FINDINGS.md) attributed the memory win to the
serverExternalPackages line and the integrations barrel diet. These three
measured at exactly zero and were most of the file churn, so they go:

next-cache-handler: orphaned. cacheHandlers and cacheComponents are both
already gone from next.config.ts and nothing imported the package — it
was reachable only through a stale dependency entry.

cache-tags / cache-invalidation: there is no "use cache", cacheTag,
cacheLife or unstable_cache anywhere in app source, so revalidateTag was
firing on tags nothing registers, with no handler to receive them, inside
a catch that swallowed the resulting out-of-scope error. Removing the
calls also removed the SELECTs that existed only to supply a board name
or member list to them, so board.ts, group.ts, user.ts, serverSettings.ts,
section-router.ts and change-search-preferences.ts return to the
merge-base exactly. The live in-process integration data cache is
untouched.

Redis 6 -> 2 client consolidation: no measurable effect at idle (139.3 vs
139.2 MiB) or under load. Reverted, but the pubSub:last:* TTL is kept —
that one is an unbounded-growth fix, not a consolidation.

23 files, -883 lines. Diff vs merge-base: 100 files/3336 insertions ->
82 files/2484. pnpm test 144/144 files, 1795 tests, 0 failures;
typecheck and lint clean; app and Docker image build.
2026-08-09 00:59:41 +02:00
ajnart 9187af8882 style: auto-fix lint and format 2026-08-08 11:02:04 +00:00
Thomas Camlong abec79abe8 fix: address CodeRabbit review — typed mocks, robust benchmark script
- Replace `as any` casts in integration-test-connection.spec.ts with
  Pick<Integration, "testConnectionAsync"> + mockResolvedValue
- build-compare.sh: split local declarations (SC2318), clean .next
  between builds with --force, add EXIT trap for safe stash/cleanup
2026-08-08 13:00:42 +02:00
Thomas Camlong 3db87d781f fix: address CodeRabbit review issues
- redis-handler: add TLS_CA and USERNAME env support matching core client
- connection.ts: fix Boolean() truthy bug (CI="false" was disabling Redis)
- channel.ts: add in-memory lock for single-instance fallback mode
- channel-subscription-tracker: lazy-init subscriber client
- board.ts: invalidate user cache on board delete for home-board users
- .env.example: document REDIS_CACHE_DB vs DATABASE_INDEX collision risk
- CLI docs: fix docker exec command (remove /bin/bash prefix)
- Docker docs: fix docker rm -f and docker ps -a for stopped containers
2026-08-08 09:17:39 +02:00
Thomas Camlong 9596a83f60 fix: use any cast for mock integration in test-connection spec
The barrel no longer exports PiHoleIntegrationV6 after the dynamic
import refactor. The mock only needs testConnectionAsync, so a type
cast is sufficient.
2026-08-08 09:03:18 +02:00
Thomas Camlong 6692acbd33 chore: strip formatting noise, delete dead routes, fix CodeRabbit issues
- Remove 179 format-only file changes (docs, migrations, plex, patchmon, CSS)
- Delete .cursor/goal files from git tracking
- Delete 4 dead /api/about routes (unused, data loaded from static JSON)
- Fix resolveAlias to use absolute paths (CodeRabbit)
- Fix Tailwind classes in Mantine app layout fallback (CodeRabbit)
- Return stale data during cache revalidation (CodeRabbit)
2026-08-08 08:45:16 +02:00
Thomas Camlong 345a4be4b0 fix: CI build errors + simplify cache handler and data cache
- Remove `export const dynamic` from 4 routes (incompatible with cacheComponents)
- Static import for redis-handler (Turbopack can't resolve relative dynamic imports)
- Simplify redis-handler: Buffer.concat, NonNullable, single connect promise
- Inline single-use helpers in integration-data-cache
- Non-greedy SVG regex to prevent ReDoS
- Inline getDevelopmentServiceAliases in next.config
- Remove restating comments
2026-08-08 00:19:27 +02:00
Thomas Camlong 4270f2f2f1 fix: address CodeRabbit review — stream tee, cache guard, base64, layout fallback
- BoardLayoutFallback: use HomarrLogoWithTitle (no board context required)
- integration-data-cache: guard cache.set with placeholder identity to
  prevent stale write-back after invalidation + test
- next-cache-handler: memoize the promise (not resolved handler) to
  prevent concurrent Redis connections
- redis-handler: tee ReadableStream before consuming in set(), hand fresh
  branch to each get() caller
- redis-handler: base64-encode body instead of number[] (3-4x smaller)
2026-08-07 22:09:11 +02:00
Thomas Camlong ffed73ba8f merge: incorporate latest dev changes into phase1-perf branch
Merge origin/dev to pick up service isolation (concurrently dev script,
turbopack aliases, instrumentation simplification), PostHog analytics,
color scheme, and other dev improvements while preserving all Phase 1
performance work (Suspense boundaries, cache components, Redis handler,
integration barrel diet, cache invalidation).
2026-08-07 21:36:48 +02:00
Thomas Camlong 33293ba1cf refactor: simplify Phase 1 infrastructure code
- cache-invalidation: extract isExpectedRevalidateError helper
- integration-data-cache: flatten early-return chain, extract eviction
- redis-handler: extract StoredEntry type, readStreamBody, ensureConnected
- svg-purify: single getOwnPropertyDescriptor lookup per prop
- session-scoped-providers: simplify auth flow
- retryable-loader: use function declarations
- translation/request: replace spread-in-reduce with for loop
2026-08-07 21:04:42 +02:00
Thomas Camlong 67ac2bbc7c fix: Redis expire guard, transferOwnership prev-owner, SVG regex
- Redis get() rejects legacy expire=0 entries and cleans them up
- transferOwnership invalidates previous owner in addition to new
- SVG regex uses non-greedy match and handles self-closing <svg />
- SVG fallback returns empty string instead of HTML wrapper
- Memory channel comment: FIFO eviction, not timestamp-based
2026-08-07 20:57:07 +02:00
Thomas Camlong fbb2eddd87 fix: concurrent stale race, Redis expire leak, missing invalidation, UI Suspense
- Fix integration data cache: set pending synchronously before creating
  fetch to prevent concurrent stale-revalidation from spawning duplicate
  upstream calls
- Redis cache handler: skip persisting entries with expire=0 (dynamic
  entries should not be stored in Redis)
- Add missing invalidation: user.changePassword, group.updateGroup,
  group.transferOwnership
- Move SearchEngineOptimization inside Suspense in root layout
- Add Suspense fallback loader to root layout
- Move searchParams resolution inside Suspense in login page
2026-08-07 20:53:36 +02:00
Thomas Camlong 38ac8f3075 fix: concurrent cache miss, SVG output, narrowed error handling, format
- Fix integration data cache: check pending promise before TTL to
  prevent returning undefined on concurrent cold-cache access
- Add concurrent-miss test (two parallel callers, one fetch)
- SVG sanitizer: extract <svg> from DOMPurify output to strip
  residual HTML wrapper
- Narrow safeRevalidateTag catch: log unexpected errors, silence only
  known "outside request scope" messages
- Run oxfmt across all packages (206 files formatted)
2026-08-07 20:48:17 +02:00
Thomas Camlong b81b90875f fix: revert DB driver to static imports, fix test regressions
- Revert createRequire() for DB drivers — require() can't resolve .ts
  files in Vitest, breaking 49 test suites
- Make cache invalidation fail-safe via try-catch around revalidateTag
  (throws outside Next.js context: tests, tasks service, websocket)
- Fix 6 integration test imports: barrel no longer exports concrete
  classes, point tests at direct source paths
- Fix Redis cache handler lint errors: remove non-null assertions,
  suppress __NEXT_BUILD_ID underscore warning
2026-08-07 20:43:07 +02:00
Thomas b32ad82850 perf: cut cold route compilation and unblock preview images (#6441) 2026-08-07 20:24:26 +02:00
Thomas Camlong 67fcd69eb8 fix: inverted tag expiration in Redis handler + saveItemOptions board name
- Fix critical bug: tag expiration comparison was inverted (< should be >)
  in Redis cache handler, which would prevent revalidateTag from clearing
  stale entries
- Pass board name to invalidateBoardCache in saveItemOptions
- Add clarifying comment about two-tier invalidation helpers
2026-08-07 19:30:58 +02:00
Thomas Camlong aeabfd3521 fix: requestMedia dual-layer cache invalidation
Mirror answerRequest pattern: invalidate both module-level integration
data cache and request-handler caches for media request list/stats.
2026-08-07 19:27:41 +02:00
Thomas Camlong 37930e44cc perf: clean integration barrel — remove 46 concrete class re-exports
- Remove all concrete integration class re-exports from barrel
- Replace instanceof check with kind check in health-monitoring
- Document cache invalidation as preparatory (cacheTag follow-up)
2026-08-07 19:22:32 +02:00
Thomas Camlong 451dc3ce39 fix: address review #4 — board name invalidation, SVG tests, test gaps
- Pass board.name to all invalidateBoardCache calls (permission mutations)
- Align integration create invalidation with update/delete
- Add initial-fetch-failure test for integration-data-cache
- Expand SVG XSS test corpus (javascript:, xlink:href, @import, onbegin)
- Forbid <style> tags in SVG sanitizer to prevent CSS injection
- Fix duplicate export in packages/common/src/index.ts
2026-08-07 19:18:18 +02:00
Thomas Camlong 4361c8dbe2 fix: address Bugbot review #3 — mutation invalidation gaps
- Dual-layer cache invalidation on interact mutations
  (dns-hole, media-requests, smart-home, indexer-manager, anchor-notes)
- importOldmarrConfig: invalidate created boards
- createBoard: invalidateUserCache when auto-setting homeBoardId
- group.savePositions: invalidate user caches for affected members
- integration.requestMedia: invalidate integration data cache
- user.delete: invalidate user cache
2026-08-07 19:14:02 +02:00
Thomas Camlong 0f8362c3da fix: address Bugbot review #2 findings
- Invalidate integration data cache from widget interact mutations
  (downloads, dns-hole, media-requests, indexer-manager, smart-home, anchor-notes)
- Redis handler getExpiration: fetch missing tags from Redis (multi-instance coherence)
- saveItemOptions: invalidate board cache after saving
- Remove unused cacheTags.app
- Fix integration data cache comment accuracy
2026-08-07 19:08:57 +02:00
Thomas Camlong 6b45aca5d5 fix: address Bugbot review findings
- Skip module-level integration cache for multi-instance (REDIS_IS_EXTERNAL)
- Add invalidateBoardCache to permission mutations (prevents future leak)
- Remove unused cacheTags.integrationData
- Replace KEYS with SCAN in Redis cache handler refreshTags()
- Document REDIS_CACHE_DB env var
2026-08-07 19:04:53 +02:00
Thomas Camlong 018f98e318 perf: implement Phase 1 homepage diet via Next.js Cache Components
P1-0: V8 heap cap (HOMARR_MAX_OLD_SPACE_SIZE, default 512MB)
P1-1: Module graph cuts
  - Dynamic integration imports (55 static → lazy loaders)
  - Conditional DB driver loading (only configured driver loaded)
  - Replace jsdom with linkedom for SVG sanitization
  - Delete eager widget barrel (55 import * as → 0)
  - Extract createRetryableLoader to @homarr/common
P1-2: Enable cacheComponents + restructure root layout
  - Suspense boundaries around session-dependent providers
  - ColorSchemeScript replaces server-side cookie reads
  - Board/manage/auth layouts wrapped in Suspense
P1-3: Cache read paths
  - Cache tag registry + invalidation helpers
  - Cache invalidation wired into all mutation routers
  - Translation deep-merge memoized per locale
P1-4: Prerendered shells + loading.tsx files
P1-5: Shared integration data cache (60s TTL, stale-while-revalidate)
P1-6: Redis-backed Next.js cache handler (@homarr/next-cache-handler)
P1-7: Redis optional (memory channel fallback), TTL on pubSub:last:*,
      Redis clients consolidated from 6 to 2
2026-08-07 18:59:47 +02:00
Thomas f76a0f240a feat(apps): allow custom URI schemes in application links (#6521) 2026-08-06 21:52:56 +02:00
Thomas 778533a62e fix: revert regressions from recent integration/releases/app-href changes (#6514) 2026-08-03 17:37:53 +02:00
Thomas b4fba47a23 fix(releases): add success flag to successful response for correct error detection (#6512) 2026-08-03 17:10:35 +02:00
Thomas 2f6b96e5ba feat: restrict integrations management to integration-full-all permission (#6509) 2026-08-03 17:04:35 +02:00
Thomas Camlong de801f3f00 perf: optimize dashboard loading and builds 2026-08-02 15:58:16 +02:00
ajnart 81a13d3b52 Merge remote-tracking branch 'origin/dev' into feat/build-performance 2026-07-31 13:38:16 +02:00
Matt Van Horn 06d9c07d22 feat: expose Navidrome as a media-server (now-playing) integration (#6070) 2026-07-31 13:30:44 +02:00
ajnart add6ee56ba fix(updates): enforce a persisted daily check 2026-07-29 06:47:07 +02:00
ajnart 6b74078766 perf(widgets): lazy load the application graph 2026-07-29 06:47:07 +02:00
Thomas 2885f7a297 fix(widgets): preserve data on integration failures (#6484) 2026-07-28 22:18:08 +02:00
Thomas 14c3fa2e76 fix(beszel): prevent live subscription memory growth (#6448)
* fix(beszel): bound live stats subscriptions

* fix(beszel): avoid blocking subscription teardown

* fix(import): map new clock options
2026-07-23 02:03:08 +02:00
Thomas cf0e469465 feat: restrict apps management to board modifiers (#6425) 2026-07-21 16:56:33 +02:00
Thomas 97652802f0 feat(immich): show random photos without album (#6424) 2026-07-21 16:10:45 +02:00
Junker der Provinz 6ac4c6a7b9 fix(search): let public dashboards use generic custom search engines (#6375) 2026-07-17 14:53:52 +02:00
Thomas 5e39c8813a feat(preferences): add enableRightClickOnWidgets user preference (#6298) 2026-07-17 14:42:19 +02:00
Thomas 8528d5068b feat(media-requests): filter requests list by status and time window (#6320) 2026-07-15 16:27:24 +02:00
Thomas 2d450239ac fix(releases): add required octokit throttling rate limit handlers (#6338) 2026-07-15 11:27:32 +02:00
Thomas 576856d634 fix(beszel): add reliable live stats and filesystem UI (#6289) 2026-07-11 17:55:04 +02:00
Thomas 13c9e5e181 fix(release): repair v1.70.0 quality failures (#6279)
* fix(release): repair v1.70.0 quality failures

* style: auto-fix lint and format
2026-07-10 21:59:57 +02:00
Thomas 4ae8d527a8 fix(releases): token not applied due to broken allowedRepoIds check (#6276) 2026-07-10 17:55:18 +02:00
Josh PrestonandThomas 33395ae226 feat(integrations): add PatchMon integration and widget (#6119)
Co-authored-by: Thomas <thomas@ajnart.fr>
2026-07-10 17:48:43 +02:00
Anil K TiwariandThomas 9fb2e37605 feat(widgets): add missing & queued media widget for Radarr and Sonarr (#6078)
Co-authored-by: Thomas <thomas@ajnart.fr>
2026-07-10 17:31:08 +02:00
Felix-AyushandThomas 2c5368dd4e feat: add Bazarr integration and subtitle status widget (#6156)
Co-authored-by: Thomas <thomas@ajnart.fr>
2026-07-10 17:29:10 +02:00
Thomas 7c152cf4fe feat(api): expose board automation endpoints (#6213)
* feat(api): expose board automation endpoints

* style: auto-fix lint and format

* test(api): cover board settings updates

* Delete .opencode/changelog/2026-07-06.md
2026-07-10 17:27:42 +02:00
Thomas 718f095cb7 fix: prevent stale persisted query cache from blocking widget data (#6273) 2026-07-10 17:19:24 +02:00
Thomas 24c5d04bfa fix(beszel): disable request handler cache for stats to make live mode work (#6244) 2026-07-10 17:16:23 +02:00
Thomas 2ada1aa249 fix(qbittorrent): handle v5 stopped torrent states for pause functionality (#6211) 2026-07-10 16:55:20 +02:00