diff --git a/packages/global/common/system/types/index.d.ts b/packages/global/common/system/types/index.d.ts index 7cc24da41473..90bcb7dfd108 100644 --- a/packages/global/common/system/types/index.d.ts +++ b/packages/global/common/system/types/index.d.ts @@ -73,6 +73,11 @@ export type FastGPTFeConfigsType = { google?: string; wechat?: string; dingtalk?: string; + wecom?: { + corpid?: string; + agentid?: string; + secret?: string; + }; microsoft?: { clientId?: string; tenantId?: string; diff --git a/packages/global/support/user/constant.ts b/packages/global/support/user/constant.ts index bd696afc7fef..8d8d24e27179 100644 --- a/packages/global/support/user/constant.ts +++ b/packages/global/support/user/constant.ts @@ -17,5 +17,6 @@ export enum OAuthEnum { wechat = 'wechat', microsoft = 'microsoft', dingtalk = 'dingtalk', + wecom = 'wecom', sso = 'sso' } diff --git a/packages/service/support/user/wecom.ts b/packages/service/support/user/wecom.ts new file mode 100644 index 000000000000..0e939409e122 --- /dev/null +++ b/packages/service/support/user/wecom.ts @@ -0,0 +1,3 @@ +export function isWecomTerminal() { + return /wxwork/i.test(navigator.userAgent); +} diff --git a/packages/web/i18n/zh-CN/login.json b/packages/web/i18n/zh-CN/login.json index 61523b51ff75..f3cc74a89ead 100644 --- a/packages/web/i18n/zh-CN/login.json +++ b/packages/web/i18n/zh-CN/login.json @@ -16,5 +16,6 @@ "register": "注册账号", "root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值", "terms": "服务协议", - "use_root_login": "使用 root 用户登录" + "use_root_login": "使用 root 用户登录", + "wecom": "企业微信" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37ee67403cc4..3e2d1ee64230 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,6 +136,9 @@ importers: '@node-rs/jieba': specifier: 1.10.0 version: 1.10.0 + '@wecom/jssdk': + specifier: ^2.2.5 + version: 2.2.5 '@xmldom/xmldom': specifier: ^0.8.10 version: 0.8.10 @@ -578,7 +581,7 @@ importers: version: 1.77.8 ts-jest: specifier: ^29.1.0 - version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3) + version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0))(typescript@5.5.3) use-context-selector: specifier: ^1.4.4 version: 1.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2) @@ -718,7 +721,7 @@ importers: version: 6.3.4 ts-jest: specifier: ^29.1.0 - version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3) + version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0))(typescript@5.5.3) ts-loader: specifier: ^9.4.3 version: 9.5.1(typescript@5.5.3)(webpack@5.92.1) @@ -2019,7 +2022,7 @@ packages: '@emotion/use-insertion-effect-with-fallbacks@1.0.1': resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: - react: '>=16.8.0' + react: 18.3.1 '@emotion/utils@1.2.1': resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==} @@ -2642,8 +2645,8 @@ packages: resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==} peerDependencies: monaco-editor: '>= 0.25.0 < 1' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: 18.3.1 + react-dom: 18.3.1 '@mongodb-js/saslprep@1.1.9': resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==} @@ -2994,8 +2997,8 @@ packages: '@reactflow/node-resizer@2.2.14': resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==} peerDependencies: - react: '>=17' - react-dom: '>=17' + react: 18.3.1 + react-dom: 18.3.1 '@reactflow/node-toolbar@1.3.14': resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==} @@ -3712,6 +3715,9 @@ packages: '@webassemblyjs/wast-printer@1.12.1': resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + '@wecom/jssdk@2.2.5': + resolution: {integrity: sha512-qOBAsfqaiYM8jZHWYs/atHSpJhsLdZVNaxHQdmEQ7ZWul/GZMt4P5VY8Nf7GII7GhG8z/k+r37Dto6qtAaRqow==} + '@xmldom/xmldom@0.8.10': resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} @@ -7134,8 +7140,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 + react: 18.3.1 + react-dom: 18.3.1 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': @@ -7790,8 +7796,8 @@ packages: react-photo-view@1.2.6: resolution: {integrity: sha512-Fq17yxkMIv0oFp7HOJr39HgCZRP6A9K5T5rixJ4flSUYT2OO3V8vNxEExjhIKgIrfmTu+mDnHYEsI9RRWi1JHw==} peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' + react: 18.3.1 + react-dom: 18.3.1 react-redux@7.2.9: resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} @@ -7809,8 +7815,8 @@ packages: resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@types/react': 18.3.1 + react: 18.3.1 peerDependenciesMeta: '@types/react': optional: true @@ -7829,8 +7835,8 @@ packages: resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@types/react': 18.3.1 + react: 18.3.1 peerDependenciesMeta: '@types/react': optional: true @@ -8859,8 +8865,8 @@ packages: resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@types/react': 18.3.1 + react: 18.3.1 peerDependenciesMeta: '@types/react': optional: true @@ -8910,8 +8916,8 @@ packages: resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@types/react': 18.3.1 + react: 18.3.1 peerDependenciesMeta: '@types/react': optional: true @@ -12916,6 +12922,8 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 + '@wecom/jssdk@2.2.5': {} + '@xmldom/xmldom@0.8.10': {} '@xtuc/ieee754@1.2.0': {} @@ -14519,7 +14527,7 @@ snapshots: eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.56.0) eslint-plugin-react: 7.34.4(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.56.0) @@ -14542,8 +14550,8 @@ snapshots: debug: 4.3.5 enhanced-resolve: 5.17.0 eslint: 8.56.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.14.0 @@ -14554,7 +14562,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -14565,7 +14573,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -14575,7 +14583,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3 @@ -18989,7 +18997,7 @@ snapshots: ts-dedent@2.2.0: {} - ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3): + ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0))(typescript@5.5.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 diff --git a/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx b/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx index a75af096ad0b..b4ffd2dce5bd 100644 --- a/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx +++ b/projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx @@ -11,7 +11,8 @@ import { useTranslation } from 'next-i18next'; import I18nLngSelector from '@/components/Select/I18nLngSelector'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import MyImage from '@fastgpt/web/components/common/Image/MyImage'; -import { useToast } from '@fastgpt/web/hooks/useToast'; +import { isWecomTerminal } from '@fastgpt/service/support/user/wecom'; +import { PUT } from '@fastgpt/service/common/api/plusRequest'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8); interface Props { @@ -29,6 +30,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => { const state = useRef(nanoid()); const redirectUri = `${location.origin}/login/provider`; const { isPc } = useSystem(); + const isWecom = isWecomTerminal(); const oAuthList = [ ...(feConfigs?.oauth?.wechat && pageType !== LoginPageTypeEnum.wechat @@ -82,6 +84,18 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => { } ] : []), + ...(feConfigs?.oauth?.wecom + ? [ + { + label: t('login:wecom'), + provider: OAuthEnum.wecom, + icon: 'common/wecom', + redirectUrl: isWecom + ? `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${feConfigs?.oauth?.wecom?.corpid}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&agentid=${feConfigs?.oauth?.wecom?.agentid}&state=${state.current}#wechat_redirect` + : `https://login.work.weixin.qq.com/wwlogin/sso/login?login_type=CorpApp&appid=${feConfigs?.oauth?.wecom?.corpid}&agentid=${feConfigs?.oauth?.wecom?.agentid}&redirect_uri=${redirectUri}&state=${state.current}` + } + ] + : []), ...(pageType !== LoginPageTypeEnum.passwordLogin ? [ { @@ -99,6 +113,20 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => { [feConfigs?.sso?.url, oAuthList.length] ); + const onClickOauth = useCallback( + async (item: any) => { + item.redirectUrl && + setLoginStore({ + provider: item.provider, + lastRoute, + state: state.current + }); + item.redirectUrl && router.replace(item.redirectUrl, '_self'); + item.pageType && setPageType(item.pageType); + }, + [lastRoute, setLoginStore, setPageType] + ); + const onClickSso = useCallback(() => { if (!feConfigs?.sso?.url) return; setLoginStore({ @@ -115,7 +143,10 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => { if (feConfigs?.sso?.autoLogin) { onClickSso(); } - }, [feConfigs?.sso?.autoLogin]); + if (isWecom) { + onClickOauth(oAuthList.find((item) => item.provider === OAuthEnum.wecom)); + } + }, [feConfigs?.sso?.autoLogin, isWecom]); return ( @@ -159,16 +190,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => { borderRadius={'sm'} fontWeight={'medium'} leftIcon={} - onClick={() => { - item.redirectUrl && - setLoginStore({ - provider: item.provider, - lastRoute, - state: state.current - }); - item.redirectUrl && router.replace(item.redirectUrl, '_self'); - item.pageType && setPageType(item.pageType); - }} + onClick={() => onClickOauth(item)} > {item.label}