feat: 企业微信扫码登录插件 (#1364)
Co-authored-by: root <root@huahua.daoyoucloud.com> Co-authored-by: TomyJan <TomyJan6@gmail.com> Reviewed-on: daoyoucloud/tachybase#1364 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: huahua <1494133104@qq.com> Co-committed-by: huahua <1494133104@qq.com>
This commit is contained in:
parent
9ffd94155a
commit
b6b94e200e
@ -1,6 +1,10 @@
|
||||
{
|
||||
"name": "@tachybase/plugin-dingtalk",
|
||||
"version": "0.21.76",
|
||||
"description": "dingding authentication.",
|
||||
"keywords": [
|
||||
"Authentication"
|
||||
],
|
||||
"main": "dist/server/index.js",
|
||||
"devDependencies": {
|
||||
"@alicloud/dingtalk": "^2.1.30",
|
||||
|
@ -0,0 +1,2 @@
|
||||
/node_modules
|
||||
/src
|
1
packages/plugins/@tachybase/plugin-work-wechat/README.md
Normal file
1
packages/plugins/@tachybase/plugin-work-wechat/README.md
Normal file
@ -0,0 +1 @@
|
||||
# @tachybase/plugin-work-wechat
|
2
packages/plugins/@tachybase/plugin-work-wechat/client.d.ts
vendored
Normal file
2
packages/plugins/@tachybase/plugin-work-wechat/client.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './dist/client';
|
||||
export { default } from './dist/client';
|
1
packages/plugins/@tachybase/plugin-work-wechat/client.js
Normal file
1
packages/plugins/@tachybase/plugin-work-wechat/client.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./dist/client/index.js');
|
24
packages/plugins/@tachybase/plugin-work-wechat/package.json
Normal file
24
packages/plugins/@tachybase/plugin-work-wechat/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@tachybase/plugin-work-wechat",
|
||||
"version": "0.21.74",
|
||||
"description": "work WeChat authentication.",
|
||||
"keywords": [
|
||||
"Authentication"
|
||||
],
|
||||
"main": "dist/server/index.js",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
"ahooks": "^3.8.0",
|
||||
"antd": "5.19.1",
|
||||
"axios": "^1.7.2",
|
||||
"react-router-dom": "^6.25.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tachybase/actions": "workspace:*",
|
||||
"@tachybase/auth": "workspace:*",
|
||||
"@tachybase/client": "workspace:*",
|
||||
"@tachybase/plugin-auth": "workspace:*",
|
||||
"@tachybase/server": "workspace:*",
|
||||
"@tachybase/test": "workspace:*"
|
||||
}
|
||||
}
|
2
packages/plugins/@tachybase/plugin-work-wechat/server.d.ts
vendored
Normal file
2
packages/plugins/@tachybase/plugin-work-wechat/server.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './dist/server';
|
||||
export { default } from './dist/server';
|
1
packages/plugins/@tachybase/plugin-work-wechat/server.js
Normal file
1
packages/plugins/@tachybase/plugin-work-wechat/server.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./dist/server/index.js');
|
@ -0,0 +1,111 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { SchemaComponent, useAPIClient, useApp } from '@tachybase/client';
|
||||
|
||||
import { CopyOutlined, LoginOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, message, Typography } from 'antd';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { useTranslation } from './locale';
|
||||
|
||||
export const AdminSettingsForm = () => {
|
||||
const { t } = useTranslation();
|
||||
const app = useApp();
|
||||
const redirectUrl = React.useMemo(() => app.getApiUrl('workWeChat:redirect'), [app]);
|
||||
const onCopy = (text) => {
|
||||
navigator.clipboard.writeText(text), message.success(t('Copied'));
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SchemaComponent
|
||||
scope={{ t }}
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
public: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
autoSignup: {
|
||||
'x-decorator': 'FormItem',
|
||||
type: 'boolean',
|
||||
title: '{{t("Sign up automatically when the user does not exist")}}',
|
||||
'x-component': 'Checkbox',
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
workWeChat: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
agentId: {
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
type: 'string',
|
||||
required: true,
|
||||
title: '{{t("Agent ID")}}',
|
||||
},
|
||||
corpId: {
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
type: 'string',
|
||||
required: true,
|
||||
title: '{{t("Corp ID")}}',
|
||||
},
|
||||
corpSecret: {
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
type: 'string',
|
||||
required: true,
|
||||
title: '{{t("Corp Secret")}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<Typography.Title level={5} style={{ fontSize: '14px' }}>
|
||||
{t('Redirect URL')}
|
||||
<span style={{ marginLeft: '2px' }}>:</span>
|
||||
</Typography.Title>
|
||||
<Input value={redirectUrl} disabled={true} addonBefore={<CopyOutlined onClick={() => onCopy(redirectUrl)} />} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SignInButton = ({ authenticator }) => {
|
||||
const [href, setHref] = React.useState();
|
||||
const { t } = useTranslation();
|
||||
const api = useAPIClient();
|
||||
const location = useLocation();
|
||||
const urlSearchParams = new URLSearchParams(location.search);
|
||||
const redirect = urlSearchParams.get('redirect');
|
||||
useEffect(() => {
|
||||
href && (window.location.href = href);
|
||||
}, [href]);
|
||||
useEffect(() => {
|
||||
const authenticatorName = urlSearchParams.get('authenticator');
|
||||
const error = urlSearchParams.get('error');
|
||||
if (authenticatorName === authenticator.name && error) {
|
||||
message.error(t(error));
|
||||
return;
|
||||
}
|
||||
});
|
||||
const onClick = async () => {
|
||||
const result = await api.request({
|
||||
method: 'post',
|
||||
url: 'workWeChat:getAuthUrl',
|
||||
headers: { 'X-Authenticator': authenticator.name },
|
||||
data: { redirect: redirect },
|
||||
});
|
||||
const url = result?.data?.data?.url;
|
||||
if (url) {
|
||||
setHref(url);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Button block icon={<LoginOutlined />} shape="round" onClick={onClick}>
|
||||
{authenticator.title ? t(authenticator.title) : t('Sign in via work WeChat')}
|
||||
</Button>
|
||||
);
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
import PluginAuthClient from '@tachybase/plugin-auth/client';
|
||||
|
||||
import { authType } from '../constants';
|
||||
import { AdminSettingsForm, SignInButton } from './components';
|
||||
|
||||
export class PluginWorkWeChatClient extends Plugin {
|
||||
async load() {
|
||||
this.app.pm.get(PluginAuthClient).registerType(authType, { components: { SignInButton, AdminSettingsForm } });
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginWorkWeChatClient;
|
@ -0,0 +1,7 @@
|
||||
import { useTranslation as useT } from '@tachybase/client';
|
||||
|
||||
import { namespace } from '../constants';
|
||||
|
||||
export function useTranslation() {
|
||||
return useT([namespace, 'client'], { nsMode: 'fallback' });
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export const authType = 'Work WeChat';
|
||||
export const namespace = 'work';
|
@ -0,0 +1,2 @@
|
||||
export * from './server';
|
||||
export { default } from './server';
|
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
"Sign in via work WeChat": "Sign in via Work WeChat",
|
||||
"Agent ID": "Agent ID",
|
||||
"Corp ID": "Corp ID",
|
||||
"Corp Secret": "Corp Secret",
|
||||
"WorkWeChat": "WorkWeChat",
|
||||
"workWeChat": "workWeChat",
|
||||
"Redirect URL": "Redirect URL",
|
||||
"Please enter the authCode": "Please enter the authCode",
|
||||
"Authorizing Login": "Authorizing Login",
|
||||
"Sign up automatically when the user does not exist": "Sign up automatically when the user does not exist",
|
||||
"User not found": "User not found",
|
||||
"Failed to get mobile": "Failed to get mobile",
|
||||
"Failed to get accessToken": "Failed to get accessToken",
|
||||
"Copied": "Copied"
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
"Sign in via work WeChat": "企业微信登录",
|
||||
"Agent ID": "Agent ID",
|
||||
"Corp ID": "企业 ID",
|
||||
"Corp Secret": "企业 Secret",
|
||||
"WorkWeChat": "企业微信",
|
||||
"workWeChat": "企业微信",
|
||||
"Redirect URL": "回调地址",
|
||||
"Please enter the authCode": "请输入authCode",
|
||||
"Authorizing Login": "授权登录中...",
|
||||
"Sign up automatically when the user does not exist": "用户不存在时自动注册",
|
||||
"User not found": "用户不存在",
|
||||
"Failed to get mobile": "未获取到手机号",
|
||||
"Failed to get accessToken": "未获取到accessToken",
|
||||
"Copied": "已复制"
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import { Context, Next } from '@tachybase/actions';
|
||||
import { AppSupervisor } from '@tachybase/server';
|
||||
|
||||
export const getAuthUrl = async (ctx: Context, next: Next) => {
|
||||
const { redirect } = ctx.action.params.values;
|
||||
const url = await ctx.auth.getAuthUrl(redirect);
|
||||
ctx.body = { url };
|
||||
await next();
|
||||
};
|
||||
|
||||
export const redirect = async (ctx: Context, next: Next) => {
|
||||
const { code, state } = ctx.request.query;
|
||||
|
||||
// 如果 state 是数组,取第一个元素
|
||||
const stateString = Array.isArray(state) ? state[0] : state;
|
||||
const search = new URLSearchParams(stateString);
|
||||
|
||||
const authenticator = search.get('name');
|
||||
const appName = search.get('app');
|
||||
const redirect = search.get('redirect') || '/admin';
|
||||
let prefix = process.env.APP_PUBLIC_PATH || '';
|
||||
if (appName && appName !== 'main') {
|
||||
const appSupervisor = AppSupervisor.getInstance();
|
||||
if (appSupervisor?.runningMode !== 'single') {
|
||||
prefix += `apps/${appName}`;
|
||||
}
|
||||
}
|
||||
const auth = await ctx.app.authManager.get(authenticator, ctx);
|
||||
if (prefix.endsWith('/')) {
|
||||
prefix = prefix.slice(0, -1);
|
||||
}
|
||||
try {
|
||||
const { token } = await auth.signIn();
|
||||
ctx.redirect(`${prefix}${redirect}?authenticator=${authenticator}&token=${token}`);
|
||||
} catch (error) {
|
||||
ctx.logger.error('Work auth error', { error });
|
||||
ctx.redirect(`${prefix}/signin?redirect=${redirect}&authenticator=${authenticator}&error=${error.message}`);
|
||||
}
|
||||
await next();
|
||||
};
|
@ -0,0 +1 @@
|
||||
export { default } from './plugin';
|
@ -0,0 +1,52 @@
|
||||
import { Gateway, Plugin } from '@tachybase/server';
|
||||
|
||||
import { authType } from '../constants';
|
||||
import { getAuthUrl, redirect } from './actions/work';
|
||||
import { WorkAuth } from './work-auth';
|
||||
|
||||
export class PluginWorkWeChatServer extends Plugin {
|
||||
async afterAdd() {}
|
||||
|
||||
async beforeLoad() {}
|
||||
|
||||
async load() {
|
||||
this.app.authManager.registerTypes(authType, {
|
||||
auth: WorkAuth,
|
||||
});
|
||||
|
||||
this.app.resourcer.define({
|
||||
name: 'workWeChat',
|
||||
actions: {
|
||||
getAuthUrl,
|
||||
redirect,
|
||||
},
|
||||
});
|
||||
|
||||
this.app.acl.allow('workWeChat', '*', 'public');
|
||||
|
||||
Gateway.getInstance().addAppSelectorMiddleware(async (ctx, next) => {
|
||||
const { req } = ctx;
|
||||
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||
const params = url.searchParams;
|
||||
const state = params.get('state');
|
||||
if (state) {
|
||||
const search = new URLSearchParams(state);
|
||||
const appName = search.get('app');
|
||||
if (appName) {
|
||||
ctx.resolvedAppName = appName;
|
||||
}
|
||||
}
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
async install() {}
|
||||
|
||||
async afterEnable() {}
|
||||
|
||||
async afterDisable() {}
|
||||
|
||||
async remove() {}
|
||||
}
|
||||
|
||||
export default PluginWorkWeChatServer;
|
@ -0,0 +1,81 @@
|
||||
import { AuthConfig, BaseAuth } from '@tachybase/auth';
|
||||
import { AuthModel } from '@tachybase/plugin-auth';
|
||||
|
||||
import { namespace } from '../constants';
|
||||
import { WorkClient } from './work-client.js';
|
||||
|
||||
export { Model } from '@tachybase/database';
|
||||
|
||||
export class WorkAuth extends BaseAuth {
|
||||
constructor(config: AuthConfig) {
|
||||
const { ctx } = config;
|
||||
super({
|
||||
...config,
|
||||
userCollection: ctx.db.getCollection('users'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate() {
|
||||
const ctx = this.ctx;
|
||||
const { code } = ctx.request.query || {};
|
||||
const codeString = Array.isArray(code) ? code[0] : code;
|
||||
|
||||
if (!codeString) {
|
||||
ctx.throw(401, ctx.t('Please enter the authCode', { ns: namespace }));
|
||||
}
|
||||
|
||||
const workClient = new WorkClient({
|
||||
clientId: this.options?.work?.corpId,
|
||||
clientSecret: this.options?.work?.corpSecret,
|
||||
ctx: this.ctx,
|
||||
});
|
||||
|
||||
const accessToken = await workClient.getAccessToken();
|
||||
if (!accessToken) {
|
||||
ctx.throw(401, ctx.t('Failed to get accessToken', { ns: namespace }));
|
||||
}
|
||||
const userInfo = await workClient.getUserInfo(accessToken, codeString);
|
||||
const { telephone, name, userid } = userInfo;
|
||||
if (!telephone) {
|
||||
ctx.throw(401, ctx.t('Failed to get telephone', { ns: namespace }));
|
||||
}
|
||||
const authenticator = this.authenticator;
|
||||
let user = await authenticator.findUser(telephone);
|
||||
if (user) {
|
||||
return user;
|
||||
}
|
||||
|
||||
user = await this.userRepository.findOne({
|
||||
filter: { phone: telephone },
|
||||
});
|
||||
if (user) {
|
||||
await authenticator.addUser(user.id, {
|
||||
through: {
|
||||
uuid: telephone,
|
||||
},
|
||||
});
|
||||
return user;
|
||||
}
|
||||
const { autoSignup } = this.options?.public || {};
|
||||
if (!autoSignup) {
|
||||
ctx.throw(401, ctx.t('User not found', { ns: namespace }));
|
||||
}
|
||||
return await authenticator.newUser(telephone, {
|
||||
nickname: name ?? null,
|
||||
phone: telephone ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
async getAuthUrl(redirect) {
|
||||
const clientId = this.options?.work?.corpId;
|
||||
const app = this.ctx.app.name;
|
||||
// const redirectUrl = encodeURIComponent('https://hua.dev.daoyoucloud.com/api/workWeChat:redirect');
|
||||
const redirectUrl = encodeURIComponent(`${this.ctx.host}${process.env.API_BASE_PATH}workWeChat:redirect`);
|
||||
// TODO: 如果后续有登录后绑定的场景,服务端需要校验 state
|
||||
const state = encodeURIComponent(
|
||||
encodeURIComponent(`redirect=${redirect}&app=${app}&name=${this.ctx.headers['x-authenticator']}`),
|
||||
);
|
||||
const url = `https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=${clientId}&agentid=${this.options?.work?.agentId}&redirect_uri=${redirectUrl}&state=${state}`;
|
||||
return url;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
import axios from 'axios';
|
||||
|
||||
interface Config {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
ctx?: any;
|
||||
}
|
||||
|
||||
export class WorkClient {
|
||||
config: Config;
|
||||
ctx: any;
|
||||
accessToken: string = '';
|
||||
tokenExpiresAt: number = 0;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.config = config;
|
||||
this.ctx = config?.ctx;
|
||||
}
|
||||
|
||||
private async fetchNewAccessToken(): Promise<void> {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${this.config.clientId}&corpsecret=${this.config.clientSecret}`,
|
||||
);
|
||||
const data = response.data;
|
||||
this.accessToken = data.access_token;
|
||||
this.tokenExpiresAt = Date.now() + data.expires_in * 1000;
|
||||
} catch (error) {
|
||||
console.error('获取 access_token 失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async getAccessToken(): Promise<string> {
|
||||
if (!this.accessToken || Date.now() > this.tokenExpiresAt) {
|
||||
await this.fetchNewAccessToken();
|
||||
}
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
async getUserInfo(accessToken: string, code: string): Promise<any> {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=${accessToken}&code=${code}`,
|
||||
);
|
||||
const userId = response.data.UserId;
|
||||
|
||||
if (userId) {
|
||||
const userDetailResponse = await axios.post(
|
||||
`https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=${accessToken}&userid=${userId}`,
|
||||
);
|
||||
return userDetailResponse.data;
|
||||
} else {
|
||||
this.ctx?.log.debug(`get userInfo error message: ${response.data.errmsg}`);
|
||||
return {};
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.response?.data?.errmsg) {
|
||||
this.ctx?.log.debug(`get userInfo error message: ${err.response.data.errmsg}`);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
117
pnpm-lock.yaml
117
pnpm-lock.yaml
@ -4024,6 +4024,42 @@ importers:
|
||||
specifier: ^0.6.2
|
||||
version: 0.6.2
|
||||
|
||||
packages/plugins/@tachybase/plugin-work-wechat:
|
||||
dependencies:
|
||||
'@ant-design/icons':
|
||||
specifier: ^5.3.7
|
||||
version: 5.3.7(react-dom@18.3.1)(react@18.3.1)
|
||||
'@tachybase/actions':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/actions
|
||||
'@tachybase/auth':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/auth
|
||||
'@tachybase/client':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/client
|
||||
'@tachybase/plugin-auth':
|
||||
specifier: workspace:*
|
||||
version: link:../plugin-auth
|
||||
'@tachybase/server':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/server
|
||||
'@tachybase/test':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/test
|
||||
ahooks:
|
||||
specifier: ^3.8.0
|
||||
version: 3.8.0(react@18.3.1)
|
||||
antd:
|
||||
specifier: 5.19.1
|
||||
version: 5.19.1(react-dom@18.3.1)(react@18.3.1)
|
||||
axios:
|
||||
specifier: ^1.7.2
|
||||
version: 1.7.2
|
||||
react-router-dom:
|
||||
specifier: ^6.25.1
|
||||
version: 6.25.1(react-dom@18.3.1)(react@18.3.1)
|
||||
|
||||
packages/plugins/@tachybase/plugin-workflow:
|
||||
dependencies:
|
||||
'@tachybase/actions':
|
||||
@ -4847,7 +4883,7 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-router: ^5.0.0 || ^6.0.0
|
||||
dependencies:
|
||||
ahooks: 3.7.8(react@18.3.1)
|
||||
ahooks: 3.7.11(react@18.3.1)
|
||||
query-string: 6.14.1
|
||||
react: 18.3.1
|
||||
react-router: 6.21.1(react@18.3.1)
|
||||
@ -5158,10 +5194,10 @@ packages:
|
||||
react-dom: '>=17.0.0'
|
||||
dependencies:
|
||||
'@ant-design/cssinjs': 1.18.2(react-dom@18.3.1)(react@18.3.1)
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
'@ctrl/tinycolor': 3.6.1
|
||||
antd: 5.19.1(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.41.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
swr: 2.2.4(react@18.3.1)
|
||||
@ -5176,12 +5212,12 @@ packages:
|
||||
dependencies:
|
||||
'@ant-design/icons': 5.3.7(react-dom@18.3.1)(react@18.3.1)
|
||||
'@ant-design/pro-provider': 2.13.5(antd@5.19.1)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
antd: 5.19.1(react-dom@18.3.1)(react@18.3.1)
|
||||
classnames: 2.5.1
|
||||
dayjs: 1.11.10
|
||||
lodash.merge: 4.6.2
|
||||
rc-util: 5.41.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
safe-stable-stringify: 2.4.3
|
||||
@ -7964,7 +8000,7 @@ packages:
|
||||
outdent: 0.5.0
|
||||
prettier: 2.8.8
|
||||
resolve-from: 5.0.0
|
||||
semver: 7.6.2
|
||||
semver: 7.6.0
|
||||
dev: true
|
||||
|
||||
/@changesets/assemble-release-plan@6.0.0:
|
||||
@ -7975,7 +8011,7 @@ packages:
|
||||
'@changesets/get-dependents-graph': 2.0.0
|
||||
'@changesets/types': 6.0.0
|
||||
'@manypkg/get-packages': 1.1.3
|
||||
semver: 7.6.2
|
||||
semver: 7.6.0
|
||||
dev: true
|
||||
|
||||
/@changesets/changelog-git@0.2.0:
|
||||
@ -8047,7 +8083,7 @@ packages:
|
||||
'@manypkg/get-packages': 1.1.3
|
||||
chalk: 2.4.2
|
||||
fs-extra: 7.0.1
|
||||
semver: 7.6.2
|
||||
semver: 7.6.0
|
||||
dev: true
|
||||
|
||||
/@changesets/get-release-plan@4.0.0:
|
||||
@ -11115,12 +11151,12 @@ packages:
|
||||
react: '>=16.9.0'
|
||||
react-dom: '>=16.9.0'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
'@babel/runtime': 7.24.7
|
||||
'@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
|
||||
classnames: 2.5.1
|
||||
rc-motion: 2.9.1(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-resize-observer: 1.4.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.41.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
dev: true
|
||||
@ -13254,7 +13290,7 @@ packages:
|
||||
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5)
|
||||
eslint: 8.55.0
|
||||
eslint-scope: 5.1.1
|
||||
semver: 7.6.2
|
||||
semver: 7.6.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@ -14416,6 +14452,24 @@ packages:
|
||||
screenfull: 5.2.0
|
||||
tslib: 2.6.2
|
||||
|
||||
/ahooks@3.8.0(react@18.3.1):
|
||||
resolution: {integrity: sha512-M01m+mxLRNNeJ/PCT3Fom26UyreTj6oMqJBetUrJnK4VNI5j6eMA543Xxo53OBXn6XibA2FXKcCCgrT6YCTtKQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.7
|
||||
dayjs: 1.11.10
|
||||
intersection-observer: 0.12.2
|
||||
js-cookie: 2.2.1
|
||||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
react-fast-compare: 3.2.2
|
||||
resize-observer-polyfill: 1.5.1
|
||||
screenfull: 5.2.0
|
||||
tslib: 2.6.3
|
||||
dev: false
|
||||
|
||||
/ajv-formats@1.6.1(ajv@7.2.4):
|
||||
resolution: {integrity: sha512-4CjkH20If1lhR5CGtqkrVg3bbOtFEG80X9v6jDOIUhbzzbB+UzPBGy8GQhUNVZ0yvMHdMpawCOcy5ydGMsagGQ==}
|
||||
peerDependencies:
|
||||
@ -15140,6 +15194,16 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
/axios@1.7.2:
|
||||
resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.6(debug@4.3.5)
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/b4a@1.6.4:
|
||||
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
|
||||
dev: true
|
||||
@ -25826,9 +25890,9 @@ packages:
|
||||
react: '>=16.0.0'
|
||||
react-dom: '>=16.0.0'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
'@babel/runtime': 7.24.7
|
||||
classnames: 2.5.1
|
||||
rc-util: 5.41.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
dev: true
|
||||
@ -26090,12 +26154,12 @@ packages:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
'@rc-component/trigger': 1.18.3(react-dom@18.3.1)(react@18.3.1)
|
||||
classnames: 2.5.1
|
||||
rc-motion: 2.9.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-overflow: 1.3.2(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.38.1(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-virtual-list: 3.11.3(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@ -26108,12 +26172,12 @@ packages:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
'@babel/runtime': 7.24.7
|
||||
'@rc-component/trigger': 2.1.1(react-dom@18.3.1)(react@18.3.1)
|
||||
classnames: 2.5.1
|
||||
rc-motion: 2.9.1(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-overflow: 1.3.2(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.41.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-virtual-list: 3.11.4(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@ -26315,10 +26379,10 @@ packages:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
classnames: 2.5.1
|
||||
rc-motion: 2.9.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.38.1(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-virtual-list: 3.11.3(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@ -26331,10 +26395,10 @@ packages:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
'@babel/runtime': 7.24.7
|
||||
classnames: 2.5.1
|
||||
rc-motion: 2.9.1(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.41.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-util: 5.43.0(react-dom@18.3.1)(react@18.3.1)
|
||||
rc-virtual-list: 3.14.2(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@ -26400,7 +26464,7 @@ packages:
|
||||
react: '>=16.9.0'
|
||||
react-dom: '>=16.9.0'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.1
|
||||
'@babel/runtime': 7.24.7
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-is: 18.3.1
|
||||
@ -26411,10 +26475,11 @@ packages:
|
||||
react: '>=16.9.0'
|
||||
react-dom: '>=16.9.0'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-is: 18.3.1
|
||||
dev: true
|
||||
|
||||
/rc-util@5.42.0(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-uxj2fkMe++/A3CTNagEljdTjZJHVFNH5EZcK9D4YAtWWTdEMglRE4VFtd0psIPUBIY+lSdqwVcIrR1oQMR07vw==}
|
||||
@ -26826,7 +26891,7 @@ packages:
|
||||
react-native:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
'@types/react-redux': 7.1.33
|
||||
hoist-non-react-statics: 3.3.2
|
||||
loose-envify: 1.4.0
|
||||
@ -27103,7 +27168,7 @@ packages:
|
||||
/redux@4.2.1:
|
||||
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.7
|
||||
|
||||
/reflect-metadata@0.1.14:
|
||||
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
|
||||
@ -28151,7 +28216,7 @@ packages:
|
||||
git-hooks-list: 3.1.0
|
||||
globby: 13.2.2
|
||||
is-plain-obj: 4.1.0
|
||||
semver: 7.6.2
|
||||
semver: 7.6.0
|
||||
sort-object-keys: 1.1.3
|
||||
dev: true
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user