mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-25 01:37:05 +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:
@@ -76,6 +76,21 @@ describe('remote proxy mount order', () => {
|
||||
expect(res.status).not.toBe(502);
|
||||
});
|
||||
|
||||
it('short-circuits /api/stacks/statuses (the sidebar status poll) for remote nodes', async () => {
|
||||
// The sidebar polls /api/stacks/statuses every few seconds; if a future
|
||||
// refactor accidentally mounted a local fast-path before the proxy, every
|
||||
// operator viewing a remote node would silently see the central instance's
|
||||
// own statuses. Asserts the same 502 short-circuit as /api/stacks.
|
||||
const res = await request(app)
|
||||
.get('/api/stacks/statuses')
|
||||
.set('Authorization', authHeader)
|
||||
.set('x-node-id', String(remoteNodeId));
|
||||
|
||||
expect(res.status).toBe(502);
|
||||
expect(res.headers['x-sencho-proxy']).toBeUndefined();
|
||||
expect(res.body?.error).toMatch(/unreachable/i);
|
||||
});
|
||||
|
||||
it('handles proxy-exempt paths locally even when x-node-id targets a remote', async () => {
|
||||
// /api/nodes/:id is in PROXY_EXEMPT_PREFIXES. The proxy must never catch
|
||||
// gateway-level concerns; otherwise a user whose default node is remote
|
||||
|
||||
Reference in New Issue
Block a user