mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-21 07:36:40 +00:00
fix(sidebar): require admin role for Schedule task and debounce search input (#1243)
The right-click Schedule task menu item and its keyboard shortcut were gated
only on isPaid, but the backend write routes under /api/scheduled-tasks
enforce requireAdmin + requirePaid on every action. Non-admin Skipper or
Admiral users would see the menu item and hit a 403 on click. The frontend
now mirrors the backend by gating Schedule task on isPaid && isAdmin so the
affordance only renders for users whose action will actually succeed.
Also adds a 120ms keystroke debounce to the sidebar search input. The
useStackListState filter rebuild was previously running on every keystroke
because <Command shouldFilter={false}> disables cmdk's own filter and the
existing 250ms timer only debounces state-invalidate events. Visible input
stays immediate via local state; the debounced emit drives the filter pass.
Adds a regression guard that /api/stacks/statuses is short-circuited by the
remote-node proxy (covers the sidebar status poll path) and updates the
sidebar feature docs to reflect the admin role requirement on Schedule task.
This commit is contained in:
@@ -21,6 +21,7 @@ interface UseSidebarContextMenuOptions {
|
||||
activeNode: Node | null | undefined;
|
||||
isPaid: boolean;
|
||||
isAdmiral: boolean;
|
||||
isAdmin: boolean;
|
||||
can: (action: PermissionAction, resourceType?: string, resourceId?: string) => boolean;
|
||||
}
|
||||
|
||||
@@ -32,6 +33,7 @@ export function useSidebarContextMenu({
|
||||
activeNode,
|
||||
isPaid,
|
||||
isAdmiral,
|
||||
isAdmin,
|
||||
can,
|
||||
}: UseSidebarContextMenuOptions) {
|
||||
const buildMenuCtx = useCallback((file: string): StackMenuCtx => {
|
||||
@@ -42,6 +44,7 @@ export function useSidebarContextMenu({
|
||||
isBusy: stackListState.isStackBusy(file),
|
||||
isPaid,
|
||||
isAdmiral,
|
||||
isAdmin,
|
||||
canDelete: can('stack:delete', 'stack', sName),
|
||||
canEditLabels: can('stack:edit', 'stack', sName),
|
||||
// POST /api/labels (the inline "New label" entry) is guarded by the
|
||||
@@ -121,9 +124,13 @@ export function useSidebarContextMenu({
|
||||
navState.setActiveView('scheduled-ops');
|
||||
},
|
||||
};
|
||||
// Handlers from useStackActions, useOverlayState, useViewNavigationState are
|
||||
// useCallback-stabilized at their owner hooks, so listing the menu surface
|
||||
// values (status maps, role/tier flags, pin state) is sufficient. Exhaustive
|
||||
// deps would force a rebuild on every parent render and defeat the memo.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
stackListState.stackStatuses, stackListState.stackPorts, isPaid, isAdmiral,
|
||||
stackListState.stackStatuses, stackListState.stackPorts, isPaid, isAdmiral, isAdmin,
|
||||
stackListState.isPinned, stackListState.labels, stackListState.stackLabelMap,
|
||||
stackListState.pin, stackListState.unpin,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user