fix(beszel): widget transparency, error handling, and cache invalidation

- Remove hardcoded background colors from Beszel grid/table widgets so
  the board's opacity slider controls the background via the parent
  card's --background-color CSS variable
- Grid: set bg="transparent" on inner Card components
- Table: override mantine-datatable default backgrounds to transparent
- Stats: make system selector toggle only visible on hover
- Stats: fix chart width/height(-1) warning by setting minHeight: 1
- All Beszel widgets: throw query errors to trigger the ErrorBoundary
  instead of silently rendering an empty/black UI
- Add retry: false to beszel-system-stats queries
- Add invalidateIntegrationCacheAsync() to clear all cached data and
  session stores for an integration when it is updated or deleted,
  preventing stale/errored data from persisting after config changes
This commit is contained in:
Thomas Camlong committed 2026-06-17 23:39:14 +02:00
1 parent 4b85afbb87
commit f83ef563b0
10 files changed
+84 -20

No files matched your search

+1
View File
@@ -15,6 +15,7 @@ export {
createGetSetChannel,
createQueryCacheChannel,
getAllQueryCacheAsync,
invalidateIntegrationCacheAsync,
} from "./lib/channel";
export { createIntegrationHistoryChannel } from "./lib/channels/history-channel";
+25
View File
@@ -261,6 +261,31 @@ 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 patterns = [`integration:${integrationId}:*`, `session-store:${integrationId}`];
let deletedCount = 0;
for (const pattern of patterns) {
let cursor = "0";
do {
const [nextCursor, keys] = await getSetClient.scan(cursor, "MATCH", pattern, "COUNT", 200);
cursor = nextCursor;
if (keys.length > 0) {
await getSetClient.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,