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",
|
"name": "@tachybase/plugin-dingtalk",
|
||||||
"version": "0.21.76",
|
"version": "0.21.76",
|
||||||
|
"description": "dingding authentication.",
|
||||||
|
"keywords": [
|
||||||
|
"Authentication"
|
||||||
|
],
|
||||||
"main": "dist/server/index.js",
|
"main": "dist/server/index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@alicloud/dingtalk": "^2.1.30",
|
"@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
|
specifier: ^0.6.2
|
||||||
version: 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:
|
packages/plugins/@tachybase/plugin-workflow:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tachybase/actions':
|
'@tachybase/actions':
|
||||||
@ -4847,7 +4883,7 @@ packages:
|
|||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react-router: ^5.0.0 || ^6.0.0
|
react-router: ^5.0.0 || ^6.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
ahooks: 3.7.8(react@18.3.1)
|
ahooks: 3.7.11(react@18.3.1)
|
||||||
query-string: 6.14.1
|
query-string: 6.14.1
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-router: 6.21.1(react@18.3.1)
|
react-router: 6.21.1(react@18.3.1)
|
||||||
@ -5158,10 +5194,10 @@ packages:
|
|||||||
react-dom: '>=17.0.0'
|
react-dom: '>=17.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ant-design/cssinjs': 1.18.2(react-dom@18.3.1)(react@18.3.1)
|
'@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
|
'@ctrl/tinycolor': 3.6.1
|
||||||
antd: 5.19.1(react-dom@18.3.1)(react@18.3.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: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
swr: 2.2.4(react@18.3.1)
|
swr: 2.2.4(react@18.3.1)
|
||||||
@ -5176,12 +5212,12 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@ant-design/icons': 5.3.7(react-dom@18.3.1)(react@18.3.1)
|
'@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)
|
'@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)
|
antd: 5.19.1(react-dom@18.3.1)(react@18.3.1)
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
dayjs: 1.11.10
|
dayjs: 1.11.10
|
||||||
lodash.merge: 4.6.2
|
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: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
safe-stable-stringify: 2.4.3
|
safe-stable-stringify: 2.4.3
|
||||||
@ -7964,7 +8000,7 @@ packages:
|
|||||||
outdent: 0.5.0
|
outdent: 0.5.0
|
||||||
prettier: 2.8.8
|
prettier: 2.8.8
|
||||||
resolve-from: 5.0.0
|
resolve-from: 5.0.0
|
||||||
semver: 7.6.2
|
semver: 7.6.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@changesets/assemble-release-plan@6.0.0:
|
/@changesets/assemble-release-plan@6.0.0:
|
||||||
@ -7975,7 +8011,7 @@ packages:
|
|||||||
'@changesets/get-dependents-graph': 2.0.0
|
'@changesets/get-dependents-graph': 2.0.0
|
||||||
'@changesets/types': 6.0.0
|
'@changesets/types': 6.0.0
|
||||||
'@manypkg/get-packages': 1.1.3
|
'@manypkg/get-packages': 1.1.3
|
||||||
semver: 7.6.2
|
semver: 7.6.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@changesets/changelog-git@0.2.0:
|
/@changesets/changelog-git@0.2.0:
|
||||||
@ -8047,7 +8083,7 @@ packages:
|
|||||||
'@manypkg/get-packages': 1.1.3
|
'@manypkg/get-packages': 1.1.3
|
||||||
chalk: 2.4.2
|
chalk: 2.4.2
|
||||||
fs-extra: 7.0.1
|
fs-extra: 7.0.1
|
||||||
semver: 7.6.2
|
semver: 7.6.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@changesets/get-release-plan@4.0.0:
|
/@changesets/get-release-plan@4.0.0:
|
||||||
@ -11115,12 +11151,12 @@ packages:
|
|||||||
react: '>=16.9.0'
|
react: '>=16.9.0'
|
||||||
react-dom: '>=16.9.0'
|
react-dom: '>=16.9.0'
|
||||||
dependencies:
|
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)
|
'@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
rc-motion: 2.9.1(react-dom@18.3.1)(react@18.3.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-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: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
dev: true
|
dev: true
|
||||||
@ -13254,7 +13290,7 @@ packages:
|
|||||||
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5)
|
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5)
|
||||||
eslint: 8.55.0
|
eslint: 8.55.0
|
||||||
eslint-scope: 5.1.1
|
eslint-scope: 5.1.1
|
||||||
semver: 7.6.2
|
semver: 7.6.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
@ -14416,6 +14452,24 @@ packages:
|
|||||||
screenfull: 5.2.0
|
screenfull: 5.2.0
|
||||||
tslib: 2.6.2
|
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):
|
/ajv-formats@1.6.1(ajv@7.2.4):
|
||||||
resolution: {integrity: sha512-4CjkH20If1lhR5CGtqkrVg3bbOtFEG80X9v6jDOIUhbzzbB+UzPBGy8GQhUNVZ0yvMHdMpawCOcy5ydGMsagGQ==}
|
resolution: {integrity: sha512-4CjkH20If1lhR5CGtqkrVg3bbOtFEG80X9v6jDOIUhbzzbB+UzPBGy8GQhUNVZ0yvMHdMpawCOcy5ydGMsagGQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -15140,6 +15194,16 @@ packages:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- 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:
|
/b4a@1.6.4:
|
||||||
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
|
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -25826,9 +25890,9 @@ packages:
|
|||||||
react: '>=16.0.0'
|
react: '>=16.0.0'
|
||||||
react-dom: '>=16.0.0'
|
react-dom: '>=16.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.5
|
'@babel/runtime': 7.24.7
|
||||||
classnames: 2.5.1
|
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: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
dev: true
|
dev: true
|
||||||
@ -26090,12 +26154,12 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
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)
|
'@rc-component/trigger': 1.18.3(react-dom@18.3.1)(react@18.3.1)
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
rc-motion: 2.9.0(react-dom@18.3.1)(react@18.3.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-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)
|
rc-virtual-list: 3.11.3(react-dom@18.3.1)(react@18.3.1)
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
@ -26108,12 +26172,12 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
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)
|
'@rc-component/trigger': 2.1.1(react-dom@18.3.1)(react@18.3.1)
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
rc-motion: 2.9.1(react-dom@18.3.1)(react@18.3.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-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)
|
rc-virtual-list: 3.11.4(react-dom@18.3.1)(react@18.3.1)
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
@ -26315,10 +26379,10 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.0
|
'@babel/runtime': 7.24.7
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
rc-motion: 2.9.0(react-dom@18.3.1)(react@18.3.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)
|
rc-virtual-list: 3.11.3(react-dom@18.3.1)(react@18.3.1)
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
@ -26331,10 +26395,10 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.5
|
'@babel/runtime': 7.24.7
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
rc-motion: 2.9.1(react-dom@18.3.1)(react@18.3.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)
|
rc-virtual-list: 3.14.2(react-dom@18.3.1)(react@18.3.1)
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 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: '>=16.9.0'
|
||||||
react-dom: '>=16.9.0'
|
react-dom: '>=16.9.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.1
|
'@babel/runtime': 7.24.7
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
react-is: 18.3.1
|
react-is: 18.3.1
|
||||||
@ -26411,10 +26475,11 @@ packages:
|
|||||||
react: '>=16.9.0'
|
react: '>=16.9.0'
|
||||||
react-dom: '>=16.9.0'
|
react-dom: '>=16.9.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.0
|
'@babel/runtime': 7.24.7
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
react-is: 18.3.1
|
react-is: 18.3.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/rc-util@5.42.0(react-dom@18.3.1)(react@18.3.1):
|
/rc-util@5.42.0(react-dom@18.3.1)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-uxj2fkMe++/A3CTNagEljdTjZJHVFNH5EZcK9D4YAtWWTdEMglRE4VFtd0psIPUBIY+lSdqwVcIrR1oQMR07vw==}
|
resolution: {integrity: sha512-uxj2fkMe++/A3CTNagEljdTjZJHVFNH5EZcK9D4YAtWWTdEMglRE4VFtd0psIPUBIY+lSdqwVcIrR1oQMR07vw==}
|
||||||
@ -26826,7 +26891,7 @@ packages:
|
|||||||
react-native:
|
react-native:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.0
|
'@babel/runtime': 7.24.7
|
||||||
'@types/react-redux': 7.1.33
|
'@types/react-redux': 7.1.33
|
||||||
hoist-non-react-statics: 3.3.2
|
hoist-non-react-statics: 3.3.2
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
@ -27103,7 +27168,7 @@ packages:
|
|||||||
/redux@4.2.1:
|
/redux@4.2.1:
|
||||||
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
|
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.0
|
'@babel/runtime': 7.24.7
|
||||||
|
|
||||||
/reflect-metadata@0.1.14:
|
/reflect-metadata@0.1.14:
|
||||||
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
|
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
|
||||||
@ -28151,7 +28216,7 @@ packages:
|
|||||||
git-hooks-list: 3.1.0
|
git-hooks-list: 3.1.0
|
||||||
globby: 13.2.2
|
globby: 13.2.2
|
||||||
is-plain-obj: 4.1.0
|
is-plain-obj: 4.1.0
|
||||||
semver: 7.6.2
|
semver: 7.6.0
|
||||||
sort-object-keys: 1.1.3
|
sort-object-keys: 1.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user