Skip to content
Merged
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
18 changes: 14 additions & 4 deletions ui/src/views/workflow/knowledge/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ import ButtonPublishHistory from './components/ButtonPublishHistory.vue'
import WorkflowImportDocumentDrawer from '@/views/knowledge-detail/library/document/import-document/WorkflowImportDocumentDrawer.vue'
import { goBack } from './navigation'
import ButtonAiGenerate from './components/ButtonAiGenerate.vue'
import perm from '@/permission/index.ts'

type KnowledgeWorkflowPermissionMethod = 'workflowEdit' | 'publish' | 'workflowExport'

function knowledgeWorkflowPermission(method: KnowledgeWorkflowPermissionMethod): boolean {
if (isSystemResource()) return Boolean(perm.knowledge.system[method]())
if (isSystemSharedResource()) return Boolean(perm.knowledge.share[method]())
if (isWorkspaceSharedResource()) return false // 工作空间侧共享知识库只读
return Boolean(perm.knowledge.workspace[method](knowledgeId))
}

defineOptions({ name: 'KnowledgeWorkflowView' })

Expand Down Expand Up @@ -471,13 +481,13 @@ onBeforeUnmount(stopAutoSave)
/>

<!-- 保存 -->
<MkTooltip content="保存" placement="bottom">
<MkTooltip v-if="knowledgeWorkflowPermission('workflowEdit')" content="保存" placement="bottom">
<el-button plain :disabled="loading" @click="handleSave" class="w-8! min-w-8!">
<MkIcon name="icon_save_outlined" />
</el-button>
</MkTooltip>
<!-- 发布 -->
<el-button type="primary" :disabled="loading" @click="handleOpenPublish">发布</el-button>
<el-button v-if="knowledgeWorkflowPermission('publish')" type="primary" :disabled="loading" @click="handleOpenPublish">发布</el-button>
<!-- 更多操作 -->
<MkDropdown trigger="click" placement="bottom-end" class="ml-2" persistent>
<el-button text class="h-7! w-7! px-0!">
Expand Down Expand Up @@ -507,7 +517,7 @@ onBeforeUnmount(stopAutoSave)
</MkDropdownItem>
</el-upload>
<!-- 导出工作流 -->
<MkDropdownItem @click="handleExportWorkflow">
<MkDropdownItem v-if="knowledgeWorkflowPermission('workflowExport')" @click="handleExportWorkflow">
<template #icon><MkIcon name="icon_export_outlined" /></template>
<span>导出工作流</span>
</MkDropdownItem>
Expand All @@ -528,7 +538,7 @@ onBeforeUnmount(stopAutoSave)
/>

<!-- 自动保存 -->
<MkDropdownItem @click.stop>
<MkDropdownItem v-if="knowledgeWorkflowPermission('workflowEdit')" @click.stop>
<template #icon><MkIcon name="icon_save_outlined" /></template>
<span>自动保存</span>
<el-switch v-model="autoSaveEnabled" class="ml-auto" size="small" @click.stop @change="handleAutoSaveChange" />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the shown diff, the change is generally reasonable, but there are several important issues to address before merging.

Critical issues

1. UI hiding is not authorization

Using v-if="knowledgeWorkflowPermission(...)" only hides controls. The handlers are still callable via keyboard shortcuts, programmatic calls, or other UI paths:

  • handleSave
  • handleOpenPublish
  • handleExportWorkflow
  • handleAutoSaveChange

Add permission checks inside the handlers as well. Server-side authorization must also enforce these rules.

function handleSave() {
  if (!knowledgeWorkflowPermission('workflowEdit')) return
  // ...
}

function handleOpenPublish() {
  if (!knowledgeWorkflowPermission('publish')) return
  // ...
}

function handleExportWorkflow() {
  if (!knowledgeWorkflowPermission('workflowExport')) return
  // ...
}

2. Auto-save may continue running after permission is lost

You hide the auto-save switch, but if autoSaveEnabled was already true, the auto-save logic may continue saving. This is especially risky for shared/read-only resources.

You should stop auto-save when edit permission becomes unavailable:

watch(
  () => knowledgeWorkflowPermission('workflowEdit'),
  (canEdit) => {
    if (!canEdit) {
      autoSaveEnabled.value = false
      stopAutoSave()
    }
  },
  { immediate: true }
)

Also guard handleAutoSaveChange and the auto-save execution path.

3. Verify knowledgeId is not a ref/computed

The helper uses:

perm.knowledge.workspace[method](knowledgeId)

If knowledgeId is a ref or computed, this passes a Ref object instead of the actual ID. In that case, use:

perm.knowledge.workspace[method](knowledgeId.value)

The same applies if isSystemResource, isSystemSharedResource, or isWorkspaceSharedResource are computed values rather than functions.

Potential behavioral issue

4. Workspace-shared resources always return false

This line:

if (isWorkspaceSharedResource()) return false // 工作空间侧共享知识库只读

blocks all three permissions, including workflowExport. If export is intended to be allowed for read-only shared resources, this is too restrictive. Confirm whether workflowExport should really be blocked here.

Minor improvements

5. Avoid repeated permission calls in the template

knowledgeWorkflowPermission(...) is called multiple times per render. Prefer computed properties:

const canEditWorkflow = computed(() => knowledgeWorkflowPermission('workflowEdit'))
const canPublish = computed(() => knowledgeWorkflowPermission('publish'))
const canExportWorkflow = computed(() => knowledgeWorkflowPermission('workflowExport'))

Then use:

<MkTooltip v-if="canEditWorkflow" ...>

This improves readability, avoids repeated permission logic, and makes watchers/guards easier.

6. Prefer extensionless import

This:

import perm from '@/permission/index.ts'

may work in Vite, but extensionless imports are more conventional and avoid TypeScript/bundler configuration issues:

import perm from '@/permission'

7. Consider a guard for missing permission methods

If perm is not fully typed or can change between versions, a missing method will throw:

perm.knowledge.workspace[method](knowledgeId)

You could defensively check:

const check = perm.knowledge.workspace[method]
return typeof check === 'function' ? Boolean(check(knowledgeId)) : false

Only do this if perm is not strongly typed.

Suggested refactor

import perm from '@/permission'

type KnowledgeWorkflowPermissionMethod =
  | 'workflowEdit'
  | 'publish'
  | 'workflowExport'

function knowledgeWorkflowPermission(
  method: KnowledgeWorkflowPermissionMethod
): boolean {
  if (isSystemResource()) {
    return Boolean(perm.knowledge.system[method]())
  }

  if (isSystemSharedResource()) {
    return Boolean(perm.knowledge.share[method]())
  }

  if (isWorkspaceSharedResource()) {
    return false
  }

  // Use knowledgeId.value if knowledgeId is a ref/computed
  return Boolean(perm.knowledge.workspace[method](knowledgeId))
}

const canEditWorkflow = computed(() => knowledgeWorkflowPermission('workflowEdit'))
const canPublish = computed(() => knowledgeWorkflowPermission('publish'))
const canExportWorkflow = computed(() => knowledgeWorkflowPermission('workflowExport'))

Overall: the diff is syntactically plausible, but the main missing piece is enforcing the same permission checks in the action handlers and auto-save lifecycle, not only in the template.

Expand Down
Loading