diff --git a/.github/workflows/preview-admin-build.yml b/.github/workflows/preview-admin-build.yml
index cb9c1d5006..9b92603e2e 100644
--- a/.github/workflows/preview-admin-build.yml
+++ b/.github/workflows/preview-admin-build.yml
@@ -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:
diff --git a/.github/workflows/test-fastgpt-pro.yaml b/.github/workflows/test-fastgpt-pro.yaml
index 7cca2b8ff5..caaef24f94 100644
--- a/.github/workflows/test-fastgpt-pro.yaml
+++ b/.github/workflows/test-fastgpt-pro.yaml
@@ -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:
diff --git a/document/content/self-host/upgrading/4-15/4150.mdx b/document/content/self-host/upgrading/4-15/4150.mdx
index 885250c97a..77506ba604 100644
--- a/document/content/self-host/upgrading/4-15/4150.mdx
+++ b/document/content/self-host/upgrading/4-15/4150.mdx
@@ -13,6 +13,8 @@ description: 'FastGPT V4.15.0 更新说明'
1. 增加父子节点选中互斥功能,解决:同时选中父子节点时,移动节点会出现抖动。
2. 调整文件注入 messages 位置,从 system 调整至 user,便于命中缓存。
+3. 非管理员/访客,触发余额不足时候,提示优化。
+4. 无创建权限时,隐藏模板功能。
## 🐛 修复
diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json
index 3b9f9d8b14..33de75d4c0 100644
--- a/document/data/doc-last-modified.json
+++ b/document/data/doc-last-modified.json
@@ -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",
diff --git a/pro b/pro
index 4aba60a784..70380fe539 160000
--- a/pro
+++ b/pro
@@ -1 +1 @@
-Subproject commit 4aba60a7842c0a4b952ba35daf93b68fd520ff81
+Subproject commit 70380fe53914fff72e256be0e3b4eb649ae1ad90
diff --git a/projects/app/src/pageComponents/dashboard/Container.tsx b/projects/app/src/pageComponents/dashboard/Container.tsx
index 4bb81e6b16..46487c3589 100644
--- a/projects/app/src/pageComponents/dashboard/Container.tsx
+++ b/projects/app/src/pageComponents/dashboard/Container.tsx
@@ -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
diff --git a/projects/app/src/pages/dashboard/agent/index.tsx b/projects/app/src/pages/dashboard/agent/index.tsx
index d4eb7712b5..7bfecbcc81 100644
--- a/projects/app/src/pages/dashboard/agent/index.tsx
+++ b/projects/app/src/pages/dashboard/agent/index.tsx
@@ -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 && }
+ {!folderDetail && isPc && hasCreatePer && }
{!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 && (
<>