wip: add check before intall plugin

pull/20014/head
NFish 1 year ago
parent d186daa131
commit 22bca1f5d3

@ -15,6 +15,7 @@ import { renderI18nObject } from '@/i18n'
import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks' import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
import Partner from '../base/badges/partner' import Partner from '../base/badges/partner'
import Verified from '../base/badges/verified' import Verified from '../base/badges/verified'
import { RiProhibitedLine } from '@remixicon/react'
export type Props = { export type Props = {
className?: string className?: string
@ -89,6 +90,10 @@ const Card = ({
text={getLocalizedText(brief)} text={getLocalizedText(brief)}
descriptionLineRows={descriptionLineRows} descriptionLineRows={descriptionLineRows}
/> />
<div className='flex gap-x-2 text-text-warning'>
<RiProhibitedLine className='h-4 w-4' />
<span className='system-sm-regular text-text-warning'>{t('plugin.installModal.installWarning')}</span>
</div>
{footer && <div>{footer}</div>} {footer && <div>{footer}</div>}
</div> </div>
) )

@ -124,7 +124,7 @@ const Installed: FC<Props> = ({
const isDifyVersionCompatible = useMemo(() => { const isDifyVersionCompatible = useMemo(() => {
if (!pluginDeclaration || !langeniusVersionInfo.current_version) return true if (!pluginDeclaration || !langeniusVersionInfo.current_version) return true
return gte(langeniusVersionInfo.current_version, pluginDeclaration?.manifest.meta.minimum_dify_version ?? '0.0.0') return gte(langeniusVersionInfo.current_version, pluginDeclaration?.manifest.meta.minimum_dify_version ?? '0.0.0')
}, [langeniusVersionInfo.current_version, pluginDeclaration?.manifest.meta.minimum_dify_version]) }, [langeniusVersionInfo.current_version, pluginDeclaration])
return ( return (
<> <>
@ -132,7 +132,7 @@ const Installed: FC<Props> = ({
<div className='system-md-regular text-text-secondary'> <div className='system-md-regular text-text-secondary'>
<p>{t(`${i18nPrefix}.readyToInstall`)}</p> <p>{t(`${i18nPrefix}.readyToInstall`)}</p>
{!isDifyVersionCompatible && ( {!isDifyVersionCompatible && (
<p className='system-md-regular text-text-secondary text-text-warning'> <p className='system-md-regular text-text-warning'>
{t('plugin.difyVersionNotCompatible', { minimalDifyVersion: pluginDeclaration?.manifest.meta.minimum_dify_version })} {t('plugin.difyVersionNotCompatible', { minimalDifyVersion: pluginDeclaration?.manifest.meta.minimum_dify_version })}
</p> </p>
)} )}

@ -1,4 +1,5 @@
import React, { useMemo, useRef, useState } from 'react' 'use client'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import { FileZip } from '@/app/components/base/icons/src/vender/solid/files' import { FileZip } from '@/app/components/base/icons/src/vender/solid/files'
import { Github } from '@/app/components/base/icons/src/vender/solid/general' import { Github } from '@/app/components/base/icons/src/vender/solid/general'
@ -13,12 +14,18 @@ import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
import { noop } from 'lodash-es' import { noop } from 'lodash-es'
import { useGlobalPublicStore } from '@/context/global-public-context' import { useGlobalPublicStore } from '@/context/global-public-context'
type InstallMethod = {
icon: React.FC<{ className?: string }>
text: string
action: string
}
const Empty = () => { const Empty = () => {
const { t } = useTranslation() const { t } = useTranslation()
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
const [selectedAction, setSelectedAction] = useState<string | null>(null) const [selectedAction, setSelectedAction] = useState<string | null>(null)
const [selectedFile, setSelectedFile] = useState<File | null>(null) const [selectedFile, setSelectedFile] = useState<File | null>(null)
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const { enable_marketplace, plugin_installation_permission } = useGlobalPublicStore(s => s.systemFeatures)
const setActiveTab = usePluginPageContext(v => v.setActiveTab) const setActiveTab = usePluginPageContext(v => v.setActiveTab)
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@ -38,6 +45,22 @@ const Empty = () => {
return t('plugin.list.notFound') return t('plugin.list.notFound')
}, [pluginList?.plugins.length, t, filters.categories.length, filters.tags.length, filters.searchQuery]) }, [pluginList?.plugins.length, t, filters.categories.length, filters.tags.length, filters.searchQuery])
const [installMethods, setInstallMethods] = useState<InstallMethod[]>([])
useEffect(() => {
const methods = []
if (enable_marketplace)
methods.push({ icon: MagicBox, text: t('plugin.source.marketplace'), action: 'marketplace' })
if (plugin_installation_permission.restrict_to_marketplace_only) {
setInstallMethods(methods)
}
else {
methods.push({ icon: Github, text: t('plugin.source.github'), action: 'github' })
methods.push({ icon: FileZip, text: t('plugin.source.local'), action: 'local' })
setInstallMethods(methods)
}
}, [plugin_installation_permission, enable_marketplace, t])
return ( return (
<div className='relative z-0 w-full grow'> <div className='relative z-0 w-full grow'>
{/* skeleton */} {/* skeleton */}
@ -70,15 +93,7 @@ const Empty = () => {
accept={SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS} accept={SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS}
/> />
<div className='flex w-full flex-col gap-y-1'> <div className='flex w-full flex-col gap-y-1'>
{[ {installMethods.map(({ icon: Icon, text, action }) => (
...(
(enable_marketplace)
? [{ icon: MagicBox, text: t('plugin.list.source.marketplace'), action: 'marketplace' }]
: []
),
{ icon: Github, text: t('plugin.list.source.github'), action: 'github' },
{ icon: FileZip, text: t('plugin.list.source.local'), action: 'local' },
].map(({ icon: Icon, text, action }) => (
<div <div
key={action} key={action}
className='flex cursor-pointer items-center gap-x-1 rounded-lg border-[0.5px] bg-components-button-secondary-bg className='flex cursor-pointer items-center gap-x-1 rounded-lg border-[0.5px] bg-components-button-secondary-bg

@ -1,6 +1,6 @@
'use client' 'use client'
import { useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { RiAddLine, RiArrowDownSLine } from '@remixicon/react' import { RiAddLine, RiArrowDownSLine } from '@remixicon/react'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
@ -22,6 +22,13 @@ import { useGlobalPublicStore } from '@/context/global-public-context'
type Props = { type Props = {
onSwitchToMarketplaceTab: () => void onSwitchToMarketplaceTab: () => void
} }
type InstallMethod = {
icon: React.FC<{ className?: string }>
text: string
action: string
}
const InstallPluginDropdown = ({ const InstallPluginDropdown = ({
onSwitchToMarketplaceTab, onSwitchToMarketplaceTab,
}: Props) => { }: Props) => {
@ -30,7 +37,7 @@ const InstallPluginDropdown = ({
const [isMenuOpen, setIsMenuOpen] = useState(false) const [isMenuOpen, setIsMenuOpen] = useState(false)
const [selectedAction, setSelectedAction] = useState<string | null>(null) const [selectedAction, setSelectedAction] = useState<string | null>(null)
const [selectedFile, setSelectedFile] = useState<File | null>(null) const [selectedFile, setSelectedFile] = useState<File | null>(null)
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const { enable_marketplace, plugin_installation_permission } = useGlobalPublicStore(s => s.systemFeatures)
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0] const file = event.target.files?.[0]
@ -54,6 +61,22 @@ const InstallPluginDropdown = ({
// console.log(res) // console.log(res)
// } // }
const [installMethods, setInstallMethods] = useState<InstallMethod[]>([])
useEffect(() => {
const methods = []
if (enable_marketplace)
methods.push({ icon: MagicBox, text: t('plugin.source.marketplace'), action: 'marketplace' })
if (plugin_installation_permission.restrict_to_marketplace_only) {
setInstallMethods(methods)
}
else {
methods.push({ icon: Github, text: t('plugin.source.github'), action: 'github' })
methods.push({ icon: FileZip, text: t('plugin.source.local'), action: 'local' })
setInstallMethods(methods)
}
}, [plugin_installation_permission, enable_marketplace, t])
return ( return (
<PortalToFollowElem <PortalToFollowElem
open={isMenuOpen} open={isMenuOpen}
@ -84,15 +107,7 @@ const InstallPluginDropdown = ({
accept={SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS} accept={SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS}
/> />
<div className='w-full'> <div className='w-full'>
{[ {installMethods.map(({ icon: Icon, text, action }) => (
...(
(enable_marketplace)
? [{ icon: MagicBox, text: t('plugin.source.marketplace'), action: 'marketplace' }]
: []
),
{ icon: Github, text: t('plugin.source.github'), action: 'github' },
{ icon: FileZip, text: t('plugin.source.local'), action: 'local' },
].map(({ icon: Icon, text, action }) => (
<div <div
key={action} key={action}
className='flex w-full !cursor-pointer items-center gap-1 rounded-lg px-2 py-1.5 hover:bg-state-base-hover' className='flex w-full !cursor-pointer items-center gap-1 rounded-lg px-2 py-1.5 hover:bg-state-base-hover'

@ -153,6 +153,7 @@ const translation = {
next: 'Next', next: 'Next',
pluginLoadError: 'Plugin load error', pluginLoadError: 'Plugin load error',
pluginLoadErrorDesc: 'This plugin will not be installed', pluginLoadErrorDesc: 'This plugin will not be installed',
installWarning: 'This plugin is not allowed to be installed.',
}, },
installFromGitHub: { installFromGitHub: {
installPlugin: 'Install plugin from GitHub', installPlugin: 'Install plugin from GitHub',

@ -136,6 +136,7 @@ const translation = {
installPlugin: 'プラグインをインストールする', installPlugin: 'プラグインをインストールする',
back: '戻る', back: '戻る',
uploadingPackage: '{{packageName}}をアップロード中...', uploadingPackage: '{{packageName}}をアップロード中...',
installWarning: 'このプラグインはインストールを許可されていません。',
}, },
installFromGitHub: { installFromGitHub: {
installedSuccessfully: 'インストールに成功しました', installedSuccessfully: 'インストールに成功しました',

@ -153,6 +153,7 @@ const translation = {
next: '下一步', next: '下一步',
pluginLoadError: '插件加载错误', pluginLoadError: '插件加载错误',
pluginLoadErrorDesc: '此插件将不会被安装', pluginLoadErrorDesc: '此插件将不会被安装',
installWarning: '此插件不允许安装。',
}, },
installFromGitHub: { installFromGitHub: {
installPlugin: '从 GitHub 安装插件', installPlugin: '从 GitHub 安装插件',

@ -13,12 +13,23 @@ export enum LicenseStatus {
LOST = 'lost', LOST = 'lost',
} }
export enum InstallationScope {
ALL = 'all',
NONE = 'none',
OFFICIAL_ONLY = 'official_only',
OFFICIAL_AND_PARTNER = 'official_and_specific_partners',
}
type License = { type License = {
status: LicenseStatus status: LicenseStatus
expired_at: string | null expired_at: string | null
} }
export type SystemFeatures = { export type SystemFeatures = {
plugin_installation_permission: {
plugin_installation_scope: InstallationScope,
restrict_to_marketplace_only: boolean
},
sso_enforced_for_signin: boolean sso_enforced_for_signin: boolean
sso_enforced_for_signin_protocol: SSOProtocol | '' sso_enforced_for_signin_protocol: SSOProtocol | ''
sso_enforced_for_web: boolean sso_enforced_for_web: boolean
@ -50,6 +61,10 @@ export type SystemFeatures = {
} }
export const defaultSystemFeatures: SystemFeatures = { export const defaultSystemFeatures: SystemFeatures = {
plugin_installation_permission: {
plugin_installation_scope: InstallationScope.ALL,
restrict_to_marketplace_only: false,
},
sso_enforced_for_signin: false, sso_enforced_for_signin: false,
sso_enforced_for_signin_protocol: '', sso_enforced_for_signin_protocol: '',
sso_enforced_for_web: false, sso_enforced_for_web: false,

Loading…
Cancel
Save