Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/cache-widget/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,26 @@ You can customize the API endpoint URL (default - `/api/cache-widget`):
<CacheWidget apiUrl="/api/custom-cache-endpoint" />
```

### Permissions

The `permissions` prop controls which UI features are available in the widget. It accepts an array of permission strings:

- `"read"` - Allows viewing cache entries and their details. If not included or set to `null`, the widget will not render.
- `"invalidate"` - Enables the "Revalidate" and "Expire" action buttons in the key details view.

```tsx
// Read-only access (default)
<CacheWidget permissions={["read"]} />

// Full access with invalidation
<CacheWidget permissions={["read", "invalidate"]} />

// Hide widget completely
<CacheWidget permissions={null} />
```

> **Important**: Permissions are **UI-only** and only control what features are visible in the widget interface. For security, you **must** configure allowed methods and routes in your API endpoints. The widget will still make API requests based on what's visible in the UI, so ensure your API routes properly validate and restrict access to sensitive operations like cache invalidation.

## Additional

You can use the widget to view all current cache entries by layer: main, ephemeral, and persistent.
Expand Down
11 changes: 8 additions & 3 deletions packages/cache-widget/src/components/details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

import React, { use } from "react";

import { type Permissions } from "../../lib/types";
import { formatBytes, formatTimestamp, formatDuration, formatDifference } from "../../lib/utils";
import { CacheKeyContext, FetchDetailsContext, SetCacheKeyContext } from "../../store/contexts";
import { Value } from "../value";
import { Loading } from "../loading";
import { ErrorMessage } from "../error";
import { Reload } from "../reload";
import { EntryActions } from "../entry-actions";

import "./details.scss";
import { EntryActions } from "../entry-actions";

export const Details: React.FC = () => {
interface DetailsProps {
permissions: Permissions;
}

export const Details: React.FC<DetailsProps> = ({ permissions }) => {
const { data, loading, error, reload } = use(FetchDetailsContext);
const setCacheKey = use(SetCacheKeyContext);
const cacheKey = use(CacheKeyContext);
Expand Down Expand Up @@ -104,7 +109,7 @@ export const Details: React.FC = () => {
)}

{data?.value && <Value value={data.value} />}
{data && <EntryActions className="__ncw_details-entry-actions" />}
{data && permissions.includes("invalidate") && <EntryActions className="__ncw_details-entry-actions" />}
</div>
</div>
);
Expand Down
2 changes: 2 additions & 0 deletions packages/cache-widget/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export type CacheWidgetData = {
keys: string[];
keyDetails: Record<string, CacheKeyInfo>;
};

export type Permissions = ("read" | "invalidate")[];
8 changes: 6 additions & 2 deletions packages/cache-widget/src/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from "react";

import { type Permissions } from "./lib/types";
import { Trigger } from "./components/trigger";
import { Dialog } from "./components/dialog";
import { CloseButton } from "./components/close-button";
Expand All @@ -13,9 +14,12 @@ import { CacheWidgetProvider } from "./store/provider";

interface CacheWidgetProps {
apiUrl?: string;
permissions?: Permissions | null;
}

export const CacheWidget: React.FC<CacheWidgetProps> = ({ apiUrl = "/api/cache-widget" }) => {
export const CacheWidget: React.FC<CacheWidgetProps> = ({ apiUrl = "/api/cache-widget", permissions = ["read"] }) => {
if (permissions === null || !permissions.includes("read")) return null;

return (
<CacheWidgetProvider apiUrl={apiUrl}>
<Trigger />
Expand All @@ -24,7 +28,7 @@ export const CacheWidget: React.FC<CacheWidgetProps> = ({ apiUrl = "/api/cache-w
<CloseButton />
<Content>
<KeysList />
<Details />
<Details permissions={permissions} />
</Content>
</Dialog>
</CacheWidgetProvider>
Expand Down