perf: template permission (#6848)

* perf: template permission

* perf: action

* doc
This commit is contained in:
Archer
2026-04-28 21:35:13 +08:00
committed by GitHub
parent aee6c0b1b0
commit 9f5e1d08b7
7 changed files with 66 additions and 50 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ permissions:
jobs:
build:
if: ${{ github.event.pull_request.author_association == 'MEMBER' }}
if: ${{ contains(fromJSON('["COLLABORATOR", "OWNER"]'), github.event.pull_request.author_association) }}
runs-on: ubuntu-24.04
steps:
+1 -1
View File
@@ -20,7 +20,7 @@ concurrency:
jobs:
test:
if: ${{ github.event_name != 'pull_request_target' || github.event.pull_request.author_association == 'MEMBER' }}
if: ${{ github.event_name != 'pull_request_target' || contains(fromJSON('["COLLABORATOR", "OWNER"]'), github.event.pull_request.author_association) }}
runs-on: ubuntu-latest
permissions:
@@ -13,6 +13,8 @@ description: 'FastGPT V4.15.0 更新说明'
1. 增加父子节点选中互斥功能,解决:同时选中父子节点时,移动节点会出现抖动。
2. 调整文件注入 messages 位置,从 system 调整至 user,便于命中缓存。
3. 非管理员/访客,触发余额不足时候,提示优化。
4. 无创建权限时,隐藏模板功能。
## 🐛 修复
+3 -2
View File
@@ -231,6 +231,7 @@
"content/self-host/upgrading/4-14/41415.mdx": "2026-04-26T21:28:27+08:00",
"content/self-host/upgrading/4-14/41416.en.mdx": "2026-04-26T21:28:27+08:00",
"content/self-host/upgrading/4-14/41416.mdx": "2026-04-26T22:41:57+08:00",
"content/self-host/upgrading/4-14/41417.mdx": "2026-04-28T18:03:38+08:00",
"content/self-host/upgrading/4-14/4142.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4142.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4143.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -251,7 +252,7 @@
"content/self-host/upgrading/4-14/41481.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4149.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4149.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-15/4150.mdx": "2026-04-28T15:10:52+08:00",
"content/self-host/upgrading/4-15/4150.mdx": "2026-04-28T21:27:07+08:00",
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -393,7 +394,7 @@
"content/self-host/upgrading/upgrade-intruction.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/upgrade-intruction.mdx": "2026-04-26T21:08:47+08:00",
"content/toc.en.mdx": "2026-04-26T21:28:27+08:00",
"content/toc.mdx": "2026-04-26T21:28:27+08:00",
"content/toc.mdx": "2026-04-28T18:03:38+08:00",
"content/use-cases/app-cases/dalle3.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/dalle3.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/english_essay_correction_bot.en.mdx": "2026-04-26T21:08:47+08:00",
+1 -1
Submodule pro updated: 4aba60a784...70380fe539
@@ -1,7 +1,7 @@
import { Box, Divider, Flex, useDisclosure } from '@chakra-ui/react';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useTranslation } from 'next-i18next';
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import { AppTemplateTypeEnum, AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRouter } from 'next/router';
@@ -36,11 +36,12 @@ const DashboardContainer = ({
}) => React.ReactNode;
}) => {
const router = useRouter();
const { t, i18n } = useTranslation();
const { t } = useTranslation();
const { isPc } = useSystem();
const { feConfigs } = useSystemStore();
const { isOpen: isOpenSidebar, onOpen: onOpenSidebar, onClose: onCloseSidebar } = useDisclosure();
const { userInfo } = useUserStore();
const hasAppCreatePer = !!userInfo?.team.permission.hasAppCreatePer;
// First tab
const currentTab = useMemo(() => {
@@ -56,10 +57,16 @@ const DashboardContainer = ({
appType?: AppTypeEnum | 'all';
};
useEffect(() => {
if (userInfo && currentTab === TabEnum.app_templates && !hasAppCreatePer) {
router.replace('/dashboard/agent');
}
}, [currentTab, hasAppCreatePer, router, userInfo]);
// Template market
const { data: templateTags = [], loading: isLoadingTemplatesTags } = useRequest(
() =>
currentTab === TabEnum.app_templates
currentTab === TabEnum.app_templates && hasAppCreatePer
? getTemplateTagList().then((res) => [
{
typeId: AppTemplateTypeEnum.recommendation,
@@ -73,20 +80,20 @@ const DashboardContainer = ({
: Promise.resolve([]),
{
manual: false,
refreshDeps: [currentTab]
refreshDeps: [currentTab, hasAppCreatePer, userInfo?.team.isWecomTeam]
}
);
const { data: templateData, loading: isLoadingTemplates } = useRequest(
() =>
currentTab === TabEnum.app_templates
currentTab === TabEnum.app_templates && hasAppCreatePer
? getTemplateMarketItemList({ type: appType })
: Promise.resolve({ list: [], total: 0 }),
{
manual: false,
refreshDeps: [currentTab, appType]
refreshDeps: [currentTab, appType, hasAppCreatePer]
}
);
const templateList = templateData?.list || [];
const templateList = useMemo(() => templateData?.list ?? [], [templateData?.list]);
const groupList = useMemo<
{
@@ -167,40 +174,44 @@ const DashboardContainer = ({
groupName: t('common:system_tools'),
children: []
},
{
groupId: TabEnum.app_templates,
groupAvatar: 'common/templateMarket',
groupName: t('common:template_market'),
children: [
...templateTags
.map((tag) => {
const templates = templateList.filter((template) =>
template.tags.includes(tag.typeId)
);
return {
...tag,
templates
};
})
.filter((tag) => tag.templates.length > 0)
.map((tag, index) => ({
typeId: tag.typeId,
typeName: t(tag.typeName as any),
isActive: index === 0 && !currentType
})),
...(feConfigs?.appTemplateCourse
? [
{
typeId: AppTemplateTypeEnum.contribute,
typeName: t('common:contribute_app_template'),
onClick: () => {
window.open(feConfigs.appTemplateCourse);
}
}
...(hasAppCreatePer
? [
{
groupId: TabEnum.app_templates,
groupAvatar: 'common/templateMarket',
groupName: t('common:template_market'),
children: [
...templateTags
.map((tag) => {
const templates = templateList.filter((template) =>
template.tags.includes(tag.typeId)
);
return {
...tag,
templates
};
})
.filter((tag) => tag.templates.length > 0)
.map((tag, index) => ({
typeId: tag.typeId,
typeName: t(tag.typeName as any),
isActive: index === 0 && !currentType
})),
...(feConfigs?.appTemplateCourse
? [
{
typeId: AppTemplateTypeEnum.contribute,
typeName: t('common:contribute_app_template'),
onClick: () => {
window.open(feConfigs.appTemplateCourse);
}
}
]
: [])
]
: [])
]
},
}
]
: []),
{
groupId: TabEnum.mcp_server,
groupAvatar: 'mcp',
@@ -223,6 +234,7 @@ const DashboardContainer = ({
feConfigs.appTemplateCourse,
feConfigs?.isPlus,
feConfigs?.show_skill,
hasAppCreatePer,
t,
templateList,
templateTags
@@ -63,6 +63,10 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
onOpen: onOpenJsonImportModal,
onClose: onCloseJsonImportModal
} = useDisclosure();
const hasCreatePer = folderDetail
? folderDetail.permission.hasWritePer && folderDetail?.type !== AppTypeEnum.httpPlugin
: userInfo?.team.permission.hasAppCreatePer;
//if there is a workflow url in the session storage, open the json import modal and import the workflow
useMount(() => {
if (getUtmWorkflow()) {
@@ -105,7 +109,7 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
overflowX={'hidden'}
>
{/* Only shown on pc root page */}
{!folderDetail && isPc && <TemplateCreatePanel type={appType} />}
{!folderDetail && isPc && hasCreatePer && <TemplateCreatePanel type={appType} />}
<Flex alignItems={'center'}>
{!isPc ? (
MenuIcon
@@ -143,10 +147,7 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
/>
)}
{(folderDetail
? folderDetail.permission.hasWritePer &&
folderDetail?.type !== AppTypeEnum.httpPlugin
: userInfo?.team.permission.hasAppCreatePer) && (
{hasCreatePer && (
<>
<Button
variant={'grayBase'}