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
34 changes: 32 additions & 2 deletions apps/application/serializers/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,6 @@ class ApplicationImportRequest(serializers.Serializer):

class ApplicationEditSerializer(serializers.Serializer):
name = serializers.CharField(required=False, max_length=64, min_length=1, label=_("Application Name"))
is_portal = serializers.BooleanField(required=False, label=_("Whether to publish on portal"))
desc = serializers.CharField(
required=False,
max_length=256,
Expand Down Expand Up @@ -1349,6 +1348,38 @@ def publish(self, instance, with_valid=True):
)
return self.one(with_valid=False)

@transaction.atomic
def portal_up(self, with_valid=True):
"""上架到门户:仅已发布的应用可上架,调用方需具备门户上架权限。"""
if with_valid:
self.is_valid()
workspace_id = self.data.get("workspace_id")
application = (
QuerySet(Application).filter(id=self.data.get("application_id"), workspace_id=workspace_id).first()
)
if not application:
raise AppApiException(500, _("Application id does not exist"))
if not application.is_publish:
raise AppApiException(500, _("Only published applications can be published to Portal"))
application.is_portal = True
application.save()
return self.one(with_valid=False)

@transaction.atomic
def portal_down(self, with_valid=True):
"""从门户下架,调用方需具备门户下架权限。"""
if with_valid:
self.is_valid()
workspace_id = self.data.get("workspace_id")
application = (
QuerySet(Application).filter(id=self.data.get("application_id"), workspace_id=workspace_id).first()
)
if not application:
raise AppApiException(500, _("Application id does not exist"))
application.is_portal = False
application.save()
return self.one(with_valid=False)

@staticmethod
def update_work_flow_model(instance):
if "nodes" not in instance.get("work_flow"):
Expand Down Expand Up @@ -1465,7 +1496,6 @@ def edit(self, instance: Dict, with_valid=True):
ToolExecutor().validate_mcp_transport(json.dumps(instance.get("mcp_servers")))
update_keys = [
"name",
"is_portal",
"desc",
"model_id",
"multiple_rounds_dialogue",
Expand Down
2 changes: 2 additions & 0 deletions apps/application/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
path('workspace/<str:workspace_id>/application/batch_clean_time', views.ApplicationAPI.BatchCleanTime.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>', views.ApplicationAPI.Operate.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/publish', views.ApplicationAPI.Publish.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/portal_up', views.ApplicationAPI.PortalUp.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/portal_down', views.ApplicationAPI.PortalDown.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/move/<str:folder_id>', views.ApplicationAPI.Move.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key', views.ApplicationKey.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/application_stats', views.ApplicationStats.as_view()),
Expand Down
781 changes: 511 additions & 270 deletions apps/application/views/application.py

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion apps/common/auth/constants/operate_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ class Operate(Enum):
CHANGE_PASSWORD = ("READ+CREATE", _("Change Password"))
SYSTEM_API_KEY = ("READ+DELETE", _("System API Key"))
PORTAL = ("READ+PORTAL", _("Portal"))
PORTAL_UP = ("READ+PORTAL_UP", _("Publish to Portal"))
PORTAL_DOWN = ("READ+PORTAL_DOWN", _("Unpublish from Portal"))

ANNOTATION_AUTH = ('ANNOTATION', _("Annotation"))
ANNOTATION_AUTH = ("ANNOTATION", _("Annotation"))
PASSWORD = ("PASSWORD", _("Password verification"))
LOCAL = ("LOCAL", _("Account login"))
CAS = ("CAS", _("CAS"))
Expand Down
50 changes: 50 additions & 0 deletions apps/common/auth/constants/permission_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,26 @@ class PermissionConstants(Enum):
),
)

APPLICATION_PORTAL_UP = (
Permission(group=Group.APPLICATION, sub_group=Group.APPLICATION, operate=Operate.PORTAL_UP, bit_index=17),
PermissionMeta(
role_list=[RoleConstants.WORKSPACE_MANAGE, RoleConstants.USER],
category=Category.WORKSPACE,
scope=[PermissionScopeConstants.WORKSPACE_RESOURCE],
resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANAGE],
),
)

APPLICATION_PORTAL_DOWN = (
Permission(group=Group.APPLICATION, sub_group=Group.APPLICATION, operate=Operate.PORTAL_DOWN, bit_index=18),
PermissionMeta(
role_list=[RoleConstants.WORKSPACE_MANAGE, RoleConstants.USER],
category=Category.WORKSPACE,
scope=[PermissionScopeConstants.WORKSPACE_RESOURCE],
resource_permission_group_list=[ResourcePermissionConst.APPLICATION_MANAGE],
),
)

# ==================== 应用文件夹 ====================
APPLICATION_FOLDER_READ = (
Permission(group=Group.APPLICATION, sub_group=Group.FOLDER, operate=Operate.READ, bit_index=0),
Expand Down Expand Up @@ -2696,6 +2716,36 @@ class PermissionConstants(Enum):
),
)

RESOURCE_APPLICATION_PORTAL_UP = (
Permission(
group=Group.SYSTEM_RES_APPLICATION,
sub_group=Group.SYSTEM_RES_APPLICATION,
operate=Operate.PORTAL_UP,
bit_index=12,
),
PermissionMeta(
role_list=[RoleConstants.ADMIN],
category=Category.RESOURCE,
is_ee=is_ee,
scope=[PermissionScopeConstants.SYSTEM],
),
)

RESOURCE_APPLICATION_PORTAL_DOWN = (
Permission(
group=Group.SYSTEM_RES_APPLICATION,
sub_group=Group.SYSTEM_RES_APPLICATION,
operate=Operate.PORTAL_DOWN,
bit_index=13,
),
PermissionMeta(
role_list=[RoleConstants.ADMIN],
category=Category.RESOURCE,
is_ee=is_ee,
scope=[PermissionScopeConstants.SYSTEM],
),
)

# 资源管理 - 应用概览
RESOURCE_APPLICATION_OVERVIEW_READ = (
Permission(
Expand Down
51 changes: 51 additions & 0 deletions apps/common/tests/test_portal_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# coding=utf-8

from django.test import SimpleTestCase

from common.auth.constants.category_constants import Category
from common.auth.constants.operate_constants import Operate
from common.auth.constants.permission_constants import PermissionConstants
from common.auth.constants.role_constants import RoleConstants


class ApplicationPortalPermissionTests(SimpleTestCase):
"""智能体门户上下架权限已注册且分组/操作位与前端一致。"""

def test_application_portal_up_permission(self):
perm = PermissionConstants.APPLICATION_PORTAL_UP.value
meta = PermissionConstants.APPLICATION_PORTAL_UP.meta
self.assertEqual(perm.group.value, "APPLICATION")
self.assertEqual(perm.operate, Operate.PORTAL_UP)
self.assertEqual(perm.bit_index, 17)
self.assertIn(RoleConstants.WORKSPACE_MANAGE, meta.role_list)
self.assertIn(RoleConstants.USER, meta.role_list)
self.assertEqual(meta.category, Category.WORKSPACE)

def test_application_portal_down_permission(self):
perm = PermissionConstants.APPLICATION_PORTAL_DOWN.value
meta = PermissionConstants.APPLICATION_PORTAL_DOWN.meta
self.assertEqual(perm.group.value, "APPLICATION")
self.assertEqual(perm.operate, Operate.PORTAL_DOWN)
self.assertEqual(perm.bit_index, 18)
self.assertEqual(meta.category, Category.WORKSPACE)

def test_resource_application_portal_up_permission(self):
perm = PermissionConstants.RESOURCE_APPLICATION_PORTAL_UP.value
meta = PermissionConstants.RESOURCE_APPLICATION_PORTAL_UP.meta
self.assertEqual(perm.group.value, "SYSTEM_RESOURCE_APPLICATION")
self.assertEqual(perm.operate, Operate.PORTAL_UP)
self.assertEqual(perm.bit_index, 12)
self.assertIn(RoleConstants.ADMIN, meta.role_list)
self.assertEqual(meta.category, Category.RESOURCE)

def test_resource_application_portal_down_permission(self):
perm = PermissionConstants.RESOURCE_APPLICATION_PORTAL_DOWN.value
meta = PermissionConstants.RESOURCE_APPLICATION_PORTAL_DOWN.meta
self.assertEqual(perm.group.value, "SYSTEM_RESOURCE_APPLICATION")
self.assertEqual(perm.operate, Operate.PORTAL_DOWN)
self.assertEqual(perm.bit_index, 13)
self.assertEqual(meta.category, Category.RESOURCE)

def test_operate_values_match_frontend(self):
self.assertEqual(Operate.PORTAL_UP.value, "READ+PORTAL_UP")
self.assertEqual(Operate.PORTAL_DOWN.value, "READ+PORTAL_DOWN")
15 changes: 15 additions & 0 deletions apps/locales/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9306,6 +9306,21 @@ msgstr ""
msgid "Publish"
msgstr ""

msgid "Publish to Portal"
msgstr ""

msgid "Unpublish from Portal"
msgstr ""

msgid "Only published applications can be published to Portal"
msgstr ""

msgid "Publishing an application to Portal"
msgstr ""

msgid "Unpublishing an application from Portal"
msgstr ""

msgid "token is required for EVENT triggers"
msgstr ""

Expand Down
15 changes: 15 additions & 0 deletions apps/locales/zh_CN/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9431,6 +9431,21 @@ msgstr "model_params_form 中的第 {index} 项的 label 字段是必填项"
msgid "Publish"
msgstr "发布"

msgid "Publish to Portal"
msgstr "上架到门户"

msgid "Unpublish from Portal"
msgstr "从门户下架"

msgid "Only published applications can be published to Portal"
msgstr "仅已发布的应用可以上架到门户"

msgid "Publishing an application to Portal"
msgstr "将应用上架到门户"

msgid "Unpublishing an application from Portal"
msgstr "将应用从门户下架"

msgid "token is required for EVENT triggers"
msgstr "事件触发器必须设置 token"

Expand Down
15 changes: 15 additions & 0 deletions apps/locales/zh_Hant/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9431,6 +9431,21 @@ msgstr "model_params_form 中的第 {index} 项的 label 字段是必填项"
msgid "Publish"
msgstr "發布"

msgid "Publish to Portal"
msgstr "上架到門戶"

msgid "Unpublish from Portal"
msgstr "從門戶下架"

msgid "Only published applications can be published to Portal"
msgstr "僅已發布的應用可以上架到門戶"

msgid "Publishing an application to Portal"
msgstr "將應用上架到門戶"

msgid "Unpublishing an application from Portal"
msgstr "將應用從門戶下架"

msgid "token is required for EVENT triggers"
msgstr "事件觸發器必須設定 token"

Expand Down
6 changes: 4 additions & 2 deletions ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,10 @@ API 对象和工作空间上下文,作为该抽屉的范围选择例外。Syst
- 知识库 Workflow API 记录为 `/<id>/action/<page>/<size>`,筛选 user_name、state;
详情和取消为 GET action/<actionId>、POST action/<actionId>/cancel。
- 智能体复制查询完整 ApplicationDetail,model 映射 model_id 后创建,不使用卡片摘要。
- Workspace 与 System 资源管理的 Application API 通过 `putApplicationPortal(applicationId, isPortal)`
向各自范围的 `application/<applicationId>` 发送 PUT 请求,仅提交 `{ is_portal }`,返回 `ApplicationDetail`。
- Workspace 与 System 资源管理的 Application API 通过 `putApplicationPortalUp(applicationId)` 与
`putApplicationPortalDown(applicationId)` 分别向各自范围的 `application/<applicationId>/portal_up`、
`application/<applicationId>/portal_down` 发送 PUT 请求(上下架接口与通用编辑接口分离),返回 `ApplicationDetail`。
上架仅允许已发布(`is_publish`)的智能体;上下架分别受门户上架/下架权限控制。
`exportApplicationSkills(applicationId, applicationName)` 仍为本地 mock,返回 `{ application_id, name }`,
不发送请求或生成 Skills 文件,真实下载接口待接入。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ const exportApplication = (applicationId: string, applicationName: string) => {
const exportApplicationSkills = (applicationId: string, applicationName: string) =>
Promise.resolve({ application_id: applicationId, name: applicationName })

/** 修改 System 资源管理智能体门户上下架状态。 */
const putApplicationPortal = (applicationId: string, isPortal: boolean) =>
put<{ is_portal: boolean }, ApplicationDetail>(`${prefix}/${applicationId}`, { is_portal: isPortal })
/** System 资源管理智能体上架到门户(仅已发布可上架)。 */
const putApplicationPortalUp = (applicationId: string) => put<Record<string, never>, ApplicationDetail>(`${prefix}/${applicationId}/portal_up`)

/** System 资源管理智能体从门户下架。 */
const putApplicationPortalDown = (applicationId: string) => put<Record<string, never>, ApplicationDetail>(`${prefix}/${applicationId}/portal_down`)

/** 获取 System 资源管理智能体详情。 */
const getApplicationDetail = (applicationId: string) => get<ApplicationDetail>(`${prefix}/${applicationId}`)
Expand Down Expand Up @@ -69,5 +71,6 @@ export default {
deleteApplication,
exportApplication,
exportApplicationSkills,
putApplicationPortal,
putApplicationPortalUp,
putApplicationPortalDown,
}
13 changes: 8 additions & 5 deletions ui/src/api/admin/workspace/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ const exportApplication = (applicationId: string, applicationName: string) => {
const exportApplicationSkills = (applicationId: string, applicationName: string) =>
Promise.resolve({ application_id: applicationId, name: applicationName })

/** 修改工作空间智能体门户上下架状态。 */
const putApplicationPortal = (applicationId: string, isPortal: boolean) => {
return put<{ is_portal: boolean }, ApplicationDetail>(`${getPrefix()}/${applicationId}`, { is_portal: isPortal })
}
/** 工作空间智能体上架到门户(仅已发布可上架)。 */
const putApplicationPortalUp = (applicationId: string) => put<Record<string, never>, ApplicationDetail>(`${getPrefix()}/${applicationId}/portal_up`)

/** 工作空间智能体从门户下架。 */
const putApplicationPortalDown = (applicationId: string) =>
put<Record<string, never>, ApplicationDetail>(`${getPrefix()}/${applicationId}/portal_down`)

/** 导入智能体文件并创建工作空间智能体。 */
const postApplicationImport = (file: File, folderId: string) => {
Expand Down Expand Up @@ -105,7 +107,8 @@ export default {
deleteApplication,
exportApplication,
exportApplicationSkills,
putApplicationPortal,
putApplicationPortalUp,
putApplicationPortalDown,
postApplication,
postApplicationImport,
putApplication,
Expand Down
2 changes: 2 additions & 0 deletions ui/src/permission/core/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ export enum Operate {
CHANGE_PASSWORD = 'READ+CREATE',
SYSTEM_API_KEY = 'READ+DELETE',
PORTAL = 'READ+PORTAL',
PORTAL_UP = 'READ+PORTAL_UP',
PORTAL_DOWN = 'READ+PORTAL_DOWN',
PASSWORD = 'PASSWORD',
LOCAL = 'LOCAL',
CAS = 'CAS',
Expand Down
4 changes: 4 additions & 0 deletions ui/src/permission/core/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ export const PermissionConstants = {
APPLICATION_TRIGGER_CREATE: p(Group.APPLICATION, Group.APPLICATION, Operate.TRIGGER_CREATE, 14),
APPLICATION_TRIGGER_EDIT: p(Group.APPLICATION, Group.APPLICATION, Operate.TRIGGER_EDIT, 15),
APPLICATION_TRIGGER_DELETE: p(Group.APPLICATION, Group.APPLICATION, Operate.TRIGGER_DELETE, 16),
APPLICATION_PORTAL_UP: p(Group.APPLICATION, Group.APPLICATION, Operate.PORTAL_UP, 17),
APPLICATION_PORTAL_DOWN: p(Group.APPLICATION, Group.APPLICATION, Operate.PORTAL_DOWN, 18),
APPLICATION_FOLDER_READ: p(Group.APPLICATION, Group.FOLDER, Operate.READ, 0),
APPLICATION_FOLDER_CREATE: p(Group.APPLICATION, Group.FOLDER, Operate.CREATE, 1),
APPLICATION_FOLDER_EDIT: p(Group.APPLICATION, Group.FOLDER, Operate.EDIT, 2),
Expand Down Expand Up @@ -323,6 +325,8 @@ export const PermissionConstants = {
RESOURCE_APPLICATION_TRIGGER_EDIT: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_RES_APPLICATION, Operate.TRIGGER_EDIT, 9),
RESOURCE_APPLICATION_TRIGGER_DELETE: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_RES_APPLICATION, Operate.TRIGGER_DELETE, 10),
RESOURCE_APPLICATION_RELATE_RESOURCE_VIEW: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_RES_APPLICATION, Operate.RELATE_VIEW, 11),
RESOURCE_APPLICATION_PORTAL_UP: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_RES_APPLICATION, Operate.PORTAL_UP, 12),
RESOURCE_APPLICATION_PORTAL_DOWN: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_RES_APPLICATION, Operate.PORTAL_DOWN, 13),
RESOURCE_APPLICATION_OVERVIEW_READ: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.READ, 0),
RESOURCE_APPLICATION_OVERVIEW_EMBED: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.EMBED, 1),
RESOURCE_APPLICATION_OVERVIEW_ACCESS: p(Group.SYSTEM_RES_APPLICATION, Group.SYSTEM_OVERVIEW, Operate.ACCESS, 2),
Expand Down
4 changes: 3 additions & 1 deletion ui/src/permission/modules/application/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { canSys } from '../../policy'
import { PermissionConstants as P,hasEdition} from '../../core'
import { PermissionConstants as P, hasEdition } from '../../core'
import { Edition } from '@/permission/core/common'

const system = {
Expand All @@ -26,6 +26,8 @@ const system = {
copy: () => canSys(P.RESOURCE_APPLICATION_COPY),
export: () => canSys(P.RESOURCE_APPLICATION_EXPORT),
publish: () => canSys(P.RESOURCE_APPLICATION_PUBLISH),
portalUp: () => canSys(P.RESOURCE_APPLICATION_PORTAL_UP),
portalDown: () => canSys(P.RESOURCE_APPLICATION_PORTAL_DOWN),
auth: () => canSys(P.RESOURCE_APPLICATION_AUTH),
relateMap: () => canSys(P.RESOURCE_APPLICATION_RELATE_RESOURCE_VIEW),
debug: () => canSys(P.RESOURCE_APPLICATION_EDIT),
Expand Down
2 changes: 2 additions & 0 deletions ui/src/permission/modules/application/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const workspace = {
copy: (id: string) => canRes(P.APPLICATION_COPY, id),
export: (id: string) => canRes(P.APPLICATION_EXPORT, id),
publish: (id: string) => canRes(P.APPLICATION_PUBLISH, id),
portalUp: (id: string) => canRes(P.APPLICATION_PORTAL_UP, id),
portalDown: (id: string) => canRes(P.APPLICATION_PORTAL_DOWN, id),
auth: (id: string) => canRes(P.APPLICATION_RESOURCE_AUTHORIZATION, id),
relateMap: (id: string) => canRes(P.APPLICATION_RELATE_RESOURCE_VIEW, id),
debug: (id: string) => canRes(P.APPLICATION_READ, id),
Expand Down
Loading
Loading