fix(frontend): apply operator write locks from the session's operating workspace (#11395)

Claude-Session: https://claude.ai/code/session_01VAj4mmm2YThZLVkivrgsbb

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
AlexRV12
2026-09-28 18:13:06 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent f4dcaf3e45
commit 5e59cefd1f
25 changed files with 58 additions and 30 deletions
@@ -106,11 +106,12 @@
import { UserDraft } from '$lib/userDraft.svelte'
import { setOpenInSessionHandoff } from './sessions/openInSessionContext'
import { getEditorStoragePath, setEditorStoragePath } from './editorStoragePathContext'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import {
useOperatingUser,
useOperatingWorkspace
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
const operatingWorkspace = useOperatingWorkspace()
const operatingUser = useOperatingUser()
@@ -118,7 +118,8 @@
import WacExportDrawer from './scripts/WacExportDrawer.svelte'
import { UserDraft } from '$lib/userDraft.svelte'
import { UserDraftDbSyncer } from '$lib/userDraftDbSyncer.svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
const triggerLock = useTriggerLock()
let {
script = $bindable(),
@@ -22,11 +22,13 @@
import Toggle from './Toggle.svelte'
import { Trash } from 'lucide-svelte'
import { DEMO_RESTRICTION_HINT, isDemoWorkspaceRestricted } from '$lib/cloud'
import { scheduleLock, triggerLock } from '$lib/operatorWriteRights'
import { useScheduleLock, useTriggerLock } from '$lib/operatorWriteRights'
import {
useOperatingWorkspace,
useOperatingUser
} from '$lib/components/operatingWorkspace.svelte'
const scheduleLock = useScheduleLock()
const triggerLock = useTriggerLock()
const operatingWorkspace = useOperatingWorkspace()
const operatingUser = useOperatingUser()
@@ -7,7 +7,7 @@
WorkspaceService
} from '$lib/gen'
import { canWrite, displayDate, getLocalSetting, storeLocalSetting } from '$lib/utils'
import { scheduleLock } from '$lib/operatorWriteRights'
import { useScheduleLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import { base } from '$app/paths'
import CenteredPage from '$lib/components/CenteredPage.svelte'
@@ -59,6 +59,7 @@
useOperatingWorkspace,
useOperatingUser
} from '$lib/components/operatingWorkspace.svelte'
const scheduleLock = useScheduleLock()
const operatingWorkspace = useOperatingWorkspace()
const operatingUser = useOperatingUser()
@@ -68,7 +68,7 @@
import MqttIcon from '$lib/components/icons/MqttIcon.svelte'
import NatsIcon from '$lib/components/icons/NatsIcon.svelte'
import TriggerModeToggle from './TriggerModeToggle.svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { getHttpRoute } from './http/utils'
import { getEmailAddress, getEmailDomain } from './email/utils'
import { TRIGGER_LIST_CONFIG, type TriggerRow } from './triggerListConfig'
@@ -78,6 +78,7 @@
useOperatingUser,
useOperatingWorkspace
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
let { triggerKind }: { triggerKind: TriggerKind } = $props()
@@ -32,13 +32,14 @@
import { twMerge } from 'tailwind-merge'
import Badge from '../common/badge/Badge.svelte'
import Tooltip from '../meltComponents/Tooltip.svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { deepEqual } from 'fast-equals'
import {
errorHandlerArgs,
slackErrorHandlerHubPathEnding
} from '../ErrorOrRecoveryHandler.svelte'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
type Props = {
triggerPath: string
@@ -12,7 +12,7 @@
import TriggerRunnablePicker from '$lib/components/triggers/TriggerRunnablePicker.svelte'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import Section from '$lib/components/Section.svelte'
import { Loader2 } from 'lucide-svelte'
@@ -46,6 +46,7 @@
useOperatingUser,
useOperatingWorkspace
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
@@ -12,7 +12,8 @@
import { emptyStringTrimmed } from '$lib/utils'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
import { RefreshCw } from 'lucide-svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
const triggerLock = useTriggerLock()
interface Props {
can_write?: boolean
@@ -10,7 +10,7 @@
import Path from '$lib/components/Path.svelte'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import { Loader2 } from 'lucide-svelte'
import Label from '$lib/components/Label.svelte'
@@ -45,6 +45,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
let drawer: Drawer | undefined = $state(undefined)
let initialPath = $state('')
@@ -14,7 +14,8 @@
useOperatingUser,
useOperatingWorkspace
} from '$lib/components/operatingWorkspace.svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
const triggerLock = useTriggerLock()
interface Props {
initialTriggerPath?: string | undefined
dirtyLocalPart?: boolean
@@ -19,7 +19,7 @@
} from '$lib/gen'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import Section from '$lib/components/Section.svelte'
import { Loader2 } from 'lucide-svelte'
import Label from '$lib/components/Label.svelte'
@@ -43,6 +43,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
let {
useDrawer = true,
@@ -10,7 +10,7 @@
import Path from '$lib/components/Path.svelte'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import { Loader2 } from 'lucide-svelte'
import Label from '$lib/components/Label.svelte'
@@ -48,6 +48,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
let drawer: Drawer | undefined = $state(undefined)
let initialPath = $state('')
@@ -12,11 +12,12 @@
import TestingBadge from '../testingBadge.svelte'
import { untrack } from 'svelte'
import TextInput from '$lib/components/text_input/TextInput.svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import {
useOperatingUser,
useOperatingWorkspace
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
initialTriggerPath?: string | undefined
@@ -29,7 +29,7 @@
generateRandomString,
sendUserToast
} from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import Section from '$lib/components/Section.svelte'
import { Loader2, Pipette, Plus } from 'lucide-svelte'
import Label from '$lib/components/Label.svelte'
@@ -82,6 +82,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
let {
useDrawer = true,
@@ -13,7 +13,7 @@
import { KafkaTriggerService, type ErrorHandler, type Retry, type TriggerMode } from '$lib/gen'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import Section from '$lib/components/Section.svelte'
import { Loader2, RotateCcw } from 'lucide-svelte'
@@ -42,6 +42,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
@@ -12,7 +12,7 @@
import TriggerRunnablePicker from '$lib/components/triggers/TriggerRunnablePicker.svelte'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import Section from '$lib/components/Section.svelte'
import { Loader2 } from 'lucide-svelte'
@@ -51,6 +51,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
@@ -10,7 +10,7 @@
} from './utils'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { Button } from '$lib/components/common'
import TextInput from '$lib/components/text_input/TextInput.svelte'
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
@@ -32,6 +32,7 @@
useOperatingUser,
useOperatingWorkspace
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
const operatingWorkspace = useOperatingWorkspace()
const operatingUser = useOperatingUser()
@@ -4,7 +4,7 @@
import type { ExtendedNativeTrigger } from './utils'
import { getServiceConfig } from './utils'
import { canWrite, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { userStore } from '$lib/stores'
import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte'
import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte'
@@ -18,6 +18,7 @@
import GoogleDriveIcon from '$lib/components/icons/GoogleDriveIcon.svelte'
import GoogleCalendarIcon from '$lib/components/icons/GoogleCalendarIcon.svelte'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
const operatingWorkspace = useOperatingWorkspace()
@@ -12,7 +12,7 @@
import { NatsTriggerService, type ErrorHandler, type Retry, type TriggerMode } from '$lib/gen'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import Section from '$lib/components/Section.svelte'
import { Loader2 } from 'lucide-svelte'
@@ -37,6 +37,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
@@ -20,7 +20,7 @@
} from '$lib/gen'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, emptyString, emptyStringTrimmed, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import Section from '$lib/components/Section.svelte'
import { Loader2 } from 'lucide-svelte'
@@ -59,6 +59,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
@@ -34,7 +34,7 @@
} from '$lib/gen'
import { enterpriseLicense } from '$lib/stores'
import { canWrite, emptyString, formatCron, sendUserToast, cronV1toV2 } from '$lib/utils'
import { scheduleLock } from '$lib/operatorWriteRights'
import { useScheduleLock } from '$lib/operatorWriteRights'
import { base } from '$lib/base'
import Section from '$lib/components/Section.svelte'
import { List, Loader2, Save, AlertTriangle } from 'lucide-svelte'
@@ -55,6 +55,7 @@
import PermissionedAsLine from '../PermissionedAsLine.svelte'
import { useActingUser } from '$lib/actingUser.svelte'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
const scheduleLock = useScheduleLock()
let {
useDrawer = true,
@@ -10,7 +10,7 @@
import Path from '$lib/components/Path.svelte'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import { Loader2 } from 'lucide-svelte'
import Label from '$lib/components/Label.svelte'
@@ -43,6 +43,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
@@ -26,7 +26,7 @@
} from '$lib/gen'
import { usedTriggerKinds } from '$lib/stores'
import { canWrite, emptySchema, emptyString, sendUserToast } from '$lib/utils'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import { withForkConflictRetry } from '$lib/utils/forkConflict'
import Section from '$lib/components/Section.svelte'
import { Loader2, X, Plus } from 'lucide-svelte'
@@ -59,6 +59,7 @@
useOperatingWorkspace,
useOperatingWorkspaceHref
} from '$lib/components/operatingWorkspace.svelte'
const triggerLock = useTriggerLock()
interface Props {
useDrawer?: boolean
+9 -6
View File
@@ -1,8 +1,9 @@
import { derived } from 'svelte/store'
import { userWorkspaces, workspaceStore } from '$lib/stores'
import { userWorkspaces } from '$lib/stores'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
/**
* Why writes of this kind are locked in the active workspace, or `undefined` when they are not —
* Why writes of this kind are locked in the operating workspace, or `undefined` when they are not —
* the shape `title` and `disabled` both want. See `docs/operator-write-rights.md`.
*
* Only `false` locks. A workspace that never configured the key and a non-operator (whose
@@ -10,8 +11,8 @@ import { userWorkspaces, workspaceStore } from '$lib/stores'
* for everyone.
*/
function writeLock(key: 'manage_schedules' | 'manage_triggers', noun: string) {
return derived([userWorkspaces, workspaceStore], ([$userWorkspaces, $workspaceStore]) => {
const settings = $userWorkspaces.find((w) => w.id === $workspaceStore)?.operator_settings
return derived([userWorkspaces, useOperatingWorkspace()], ([$userWorkspaces, $workspace]) => {
const settings = $userWorkspaces.find((w) => w.id === $workspace)?.operator_settings
// Worded as the server words its refusal of the same write.
return settings?.[key] === false
? `Operators cannot manage ${noun} in this workspace`
@@ -19,5 +20,7 @@ function writeLock(key: 'manage_schedules' | 'manage_triggers', noun: string) {
})
}
export const scheduleLock = writeLock('manage_schedules', 'schedules')
export const triggerLock = writeLock('manage_triggers', 'triggers')
/** Reads context: call during component initialisation. */
export const useScheduleLock = () => writeLock('manage_schedules', 'schedules')
/** Reads context: call during component initialisation. */
export const useTriggerLock = () => writeLock('manage_triggers', 'triggers')
@@ -9,7 +9,7 @@
} from '$lib/components/triggers/native/utils'
import NativeTriggerTable from '$lib/components/triggers/native/NativeTriggerTable.svelte'
import NativeTriggerEditor from '$lib/components/triggers/native/NativeTriggerEditor.svelte'
import { triggerLock } from '$lib/operatorWriteRights'
import { useTriggerLock } from '$lib/operatorWriteRights'
import {
sendUserToast,
removeTriggerKindIfUnused,
@@ -29,6 +29,7 @@
import { page } from '$app/state'
import Toggle from '$lib/components/Toggle.svelte'
import ListFilters from '$lib/components/home/ListFilters.svelte'
const triggerLock = useTriggerLock()
type TriggerW = ExtendedNativeTrigger & { marked?: any }