From ccfeb178fcfd21ce1340276fd08f65a3ebc7d94f Mon Sep 17 00:00:00 2001 From: TomyJan Date: Tue, 10 Sep 2024 11:00:16 +0800 Subject: [PATCH] feat(plugin-wechat-auth): wechat qr login (#1516) Reviewed-on: https://git.daoyoucloud.com/daoyoucloud/tachybase/pulls/1516 Reviewed-by: sealday Co-authored-by: TomyJan Co-committed-by: TomyJan --- package.json | 3 + .../@tachybase/plugin-wechat-auth/.npmignore | 2 + .../@tachybase/plugin-wechat-auth/README.md | 14 + .../@tachybase/plugin-wechat-auth/client.d.ts | 2 + .../@tachybase/plugin-wechat-auth/client.js | 1 + .../plugin-wechat-auth/package.json | 28 ++ .../@tachybase/plugin-wechat-auth/server.d.ts | 2 + .../@tachybase/plugin-wechat-auth/server.js | 1 + .../src/client/admin-settings-form.tsx | 73 ++++ .../plugin-wechat-auth/src/client/index.tsx | 20 + .../src/client/sign-in-form.tsx | 106 +++++ .../src/client/wechat-qr-component.tsx | 16 + .../plugin-wechat-auth/src/constants.ts | 7 + .../plugin-wechat-auth/src/index.ts | 2 + .../plugin-wechat-auth/src/locale/en-US.ts | 9 + .../plugin-wechat-auth/src/locale/index.ts | 7 + .../plugin-wechat-auth/src/locale/zh-CN.ts | 9 + .../src/server/actions/get-auth-cfg.ts | 7 + .../src/server/actions/index.ts | 2 + .../src/server/actions/redirect.ts | 32 ++ .../plugin-wechat-auth/src/server/index.ts | 1 + .../plugin-wechat-auth/src/server/plugin.ts | 53 +++ .../src/server/wechat-auth.ts | 71 +++ .../plugin-wechat-auth/src/swagger/index.ts | 157 +++++++ pnpm-lock.yaml | 405 ++++++++++++++---- 25 files changed, 938 insertions(+), 92 deletions(-) create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/.npmignore create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/README.md create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/client.d.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/client.js create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/package.json create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/server.d.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/server.js create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/client/admin-settings-form.tsx create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/client/index.tsx create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/client/sign-in-form.tsx create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/client/wechat-qr-component.tsx create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/constants.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/index.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/locale/en-US.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/locale/index.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/locale/zh-CN.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/get-auth-cfg.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/index.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/redirect.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/server/index.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/server/plugin.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/server/wechat-auth.ts create mode 100644 packages/plugins/@tachybase/plugin-wechat-auth/src/swagger/index.ts diff --git a/package.json b/package.json index 33e106f63..b532bfedf 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,9 @@ "auto-changelog": "^2.4.0", "axios": "^1.6.8", "commander": "^9.5.0", + "eslint": "^9.10.0", "eslint-plugin-jest-dom": "^5.4.0", + "eslint-plugin-react": "^7.33.0", "eslint-plugin-testing-library": "^5.11.1", "globals": "^15.9.0", "husky": "^9.0.11", @@ -79,6 +81,7 @@ "react-dom": "^18.3.1", "tsx": "^4.15.2", "typescript": "5.4.5", + "typescript-eslint": "^8.4.0", "umi": "^4.3.3", "vitest": "^1.6.0" }, diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/.npmignore b/packages/plugins/@tachybase/plugin-wechat-auth/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/README.md b/packages/plugins/@tachybase/plugin-wechat-auth/README.md new file mode 100644 index 000000000..d9fde6675 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/README.md @@ -0,0 +1,14 @@ +# wechat-auth + +English | [中文](./README.zh-CN.md) + +## Install + +``` +pnpm pm add @tachybase/plugin-wechat-auth +pnpm pm enable @tachybase/plugin-wechat-auth +``` + +## Usage + +TODO diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/client.d.ts b/packages/plugins/@tachybase/plugin-wechat-auth/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/client.js b/packages/plugins/@tachybase/plugin-wechat-auth/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/package.json b/packages/plugins/@tachybase/plugin-wechat-auth/package.json new file mode 100644 index 000000000..84bb36a99 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tachybase/plugin-wechat-auth", + "displayName": "Auth: WeChat", + "version": "0.21.88", + "description": "WeChat authentication.", + "keywords": [ + "Authentication" + ], + "license": "Apache-2.0", + "main": "dist/server/index.js", + "devDependencies": { + "@ant-design/icons": "~5.3.7", + "antd": "5.19.4", + "react-i18next": "^15.0.1", + "react-router-dom": "^6.25.1" + }, + "peerDependencies": { + "@tachybase/actions": "workspace:*", + "@tachybase/auth": "workspace:*", + "@tachybase/client": "workspace:*", + "@tachybase/database": "workspace:*", + "@tachybase/plugin-auth": "workspace:*", + "@tachybase/schema": "workspace:*", + "@tachybase/server": "workspace:*" + }, + "description.zh-CN": "通过微信扫码认证身份。", + "displayName.zh-CN": "认证:微信" +} diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/server.d.ts b/packages/plugins/@tachybase/plugin-wechat-auth/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/server.js b/packages/plugins/@tachybase/plugin-wechat-auth/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/client/admin-settings-form.tsx b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/admin-settings-form.tsx new file mode 100644 index 000000000..efa8461fb --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/admin-settings-form.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { SchemaComponent, useApp } from '@tachybase/client'; + +import { CopyOutlined } from '@ant-design/icons'; +import { Input, message, Typography } from 'antd'; + +import { useTranslation } from '../locale'; + +export const AdminSettingsForm = () => { + const { t } = useTranslation(); + const app = useApp(); + const redirectUrl = React.useMemo(() => app.getApiUrl('wechatAuth:redirect'), [app]); + const onCopy = (text) => { + navigator.clipboard + .writeText(text) + .then(() => { + message.success(t('Copied')); + }) + .catch(() => { + message.error(t('Failed to copy')); + }); + }; + return ( +
+ +
+ + {t('Redirect URL')} + : + + onCopy(redirectUrl)} />} /> +
+
+ ); +}; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/client/index.tsx b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/index.tsx new file mode 100644 index 000000000..6497621b8 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/index.tsx @@ -0,0 +1,20 @@ +import { Plugin } from '@tachybase/client'; +import AuthPlugin from '@tachybase/plugin-auth/client'; + +import { authType } from '../constants'; +import { AdminSettingsForm } from './admin-settings-form'; +import { SignInForm } from './sign-in-form'; + +export class WeChatAuthPlugin extends Plugin { + async load() { + const auth = this.app.pm.get(AuthPlugin); + auth.registerType(authType, { + components: { + SignInForm, + AdminSettingsForm, + }, + }); + } +} + +export default WeChatAuthPlugin; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/client/sign-in-form.tsx b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/sign-in-form.tsx new file mode 100644 index 000000000..ced73e9bf --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/sign-in-form.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useState } from 'react'; +import { SchemaComponent, useAPIClient, useGlobalTheme } from '@tachybase/client'; +import { Authenticator } from '@tachybase/plugin-auth/client'; +import { ISchema } from '@tachybase/schema'; + +import { useLocation } from 'react-router-dom'; + +import { useTranslation } from '../locale'; +import WeChatQrComponent from './wechat-qr-component'; + +const qrForm: ISchema = { + type: 'object', + name: 'qrForm', + 'x-component': 'Form', + properties: { + qrCode: { + type: 'string', + 'x-component': 'WeChatQrComponent', + 'x-component-props': { + style: { width: '100%' }, + value: { config: '{{ wcQrCfg }}' }, + }, + 'x-decorator': 'FormItem', + }, + tip: { + type: 'void', + 'x-component': 'div', + 'x-content': '{{t("User will be registered automatically if not exists.")}}', + 'x-component-props': { style: { color: '#ccc' } }, + 'x-visible': '{{ autoSignUp }}', + }, + }, +}; + +export const SignInForm = (props: { authenticator: Authenticator }) => { + const authenticator = props.authenticator; + const { name, options } = authenticator; + const autoSignUp = !!options?.autoSignUp; + const { t } = useTranslation(); + const api = useAPIClient(); + const location = useLocation(); + const urlSearchParams = new URLSearchParams(location.search); + const redirect = urlSearchParams.get('redirect'); + const { theme } = useGlobalTheme(); + const isDark = theme.name.toLowerCase().includes('dark'); + const wcQrStyle = isDark ? 'white' : 'black'; + + const [wcQrCfg, setWcQrCfg] = useState(null); + + useEffect(() => { + const fetchAuthConfig = async () => { + try { + const result = await api.request({ + method: 'post', + url: 'wechatAuth:getAuthCfg', + headers: { 'X-Authenticator': authenticator.name }, + data: { redirect }, + }); + const cfg = result?.data?.data; + + const wcQrCfg = { + self_redirect: false, + id: 'wechat_qr_container', + appid: cfg.appId, + scope: cfg.scope, + redirect_uri: cfg.redirectUrl, + state: cfg.state, + style: wcQrStyle, + stylelite: 1, + fast_login: 1, + href: '', + }; + + setWcQrCfg(wcQrCfg); + + const script = document.createElement('script'); + script.async = true; + script.innerHTML = ` + (function() { + var wxScript = document.createElement('script'); + wxScript.src = '//res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'; + wxScript.async = true; + wxScript.onload = function() { + new WxLogin(${JSON.stringify(wcQrCfg)}); + }; + document.body.appendChild(wxScript); + })(); + `; + document.body.appendChild(script); + + return () => { + document.body.removeChild(script); + }; + } catch (error) { + console.error('Error fetching auth config:', error); + } + }; + + fetchAuthConfig(); + }, [api, authenticator.name, redirect, wcQrStyle]); + + if (!wcQrCfg) { + return
Loading...
; + } + return ; +}; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/client/wechat-qr-component.tsx b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/wechat-qr-component.tsx new file mode 100644 index 000000000..f89f01282 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/client/wechat-qr-component.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { css } from '@tachybase/client'; + +export default function WeChatQrComponent() { + return ( +
+
+
+ ); +} diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/constants.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/constants.ts new file mode 100644 index 000000000..b9b89c225 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/constants.ts @@ -0,0 +1,7 @@ +// @ts-ignore +import { name } from '../package.json'; + +export const authType = 'WeChat'; +export const namespace = name; +export const weChatApiOauthBaseUrl = 'https://api.weixin.qq.com/sns/oauth2/'; +export const weChatApiOauthScope = 'snsapi_login'; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/index.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/en-US.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/en-US.ts new file mode 100644 index 000000000..454450330 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/en-US.ts @@ -0,0 +1,9 @@ +export default { + "Sign in via WeChat": "Sign in via WeChat", + "Redirect URL": "Redirect URL", + "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 accessToken": "Failed to get accessToken", + "Copied": "Copied", + "Failed to copy": "Failed to copy" +} diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/index.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/index.ts new file mode 100644 index 000000000..5d46f3d45 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/index.ts @@ -0,0 +1,7 @@ +import { useTranslation as t } from 'react-i18next'; +import { namespace } from '../constants'; + +export function useTranslation() { + return t(namespace); +} + diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/zh-CN.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/zh-CN.ts new file mode 100644 index 000000000..9ac5273d3 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/locale/zh-CN.ts @@ -0,0 +1,9 @@ +export default { + "Sign in via WeChat": "微信登录", + "Redirect URL": "回调地址", + "Sign up automatically when the user does not exist": "用户不存在时自动注册", + "User not found": "用户不存在", + "Failed to get accessToken": "未获取到accessToken", + "Copied": "已复制", + "Failed to copy": "复制失败" +} diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/get-auth-cfg.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/get-auth-cfg.ts new file mode 100644 index 000000000..3bf8ed80e --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/get-auth-cfg.ts @@ -0,0 +1,7 @@ +import { Context, Next } from '@tachybase/actions'; + +export const getAuthCfg = async (ctx: Context, next: Next) => { + const { redirect } = ctx.action.params.values; + ctx.body = await ctx.auth.getAuthCfg(redirect); + await next(); +}; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/index.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/index.ts new file mode 100644 index 000000000..4fe577afd --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/index.ts @@ -0,0 +1,2 @@ +export { getAuthCfg } from './get-auth-cfg'; +export { redirect } from './redirect'; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/redirect.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/redirect.ts new file mode 100644 index 000000000..9f49bda46 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/actions/redirect.ts @@ -0,0 +1,32 @@ +import { Context, Next } from '@tachybase/actions'; +import { AppSupervisor } from '@tachybase/server'; + +export const redirect = async (ctx: Context, next: Next) => { + let { state } = ctx.request.query; + + state = Array.isArray(state) ? state[0] : state; + const search = new URLSearchParams(state); + + 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('WeChat auth error', { error }); + ctx.redirect(`${prefix}/signin?redirect=${redirect}&authenticator=${authenticator}&error=${error.message}`); + } + await next(); +}; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/server/index.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/server/plugin.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/plugin.ts new file mode 100644 index 000000000..cf16c7119 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/plugin.ts @@ -0,0 +1,53 @@ +import { Gateway, InstallOptions, Plugin } from '@tachybase/server'; + +import { authType } from '../constants'; +import { getAuthCfg, redirect } from './actions'; +import { WeChatAuth } from './wechat-auth'; + +export class WeChatAuthPlugin extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() { + this.app.authManager.registerTypes(authType, { + auth: WeChatAuth, + }); + + this.app.resourcer.define({ + name: 'wechatAuth', + actions: { + getAuthCfg, + redirect, + }, + }); + + this.app.acl.allow('wechatAuth', '*', '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) { + return next(); + } + const search = new URLSearchParams(state); + const appName = search.get('app'); + if (appName) { + ctx.resolvedAppName = appName; + } + await next(); + }); + } + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default WeChatAuthPlugin; diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/server/wechat-auth.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/wechat-auth.ts new file mode 100644 index 000000000..6b78e578f --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/server/wechat-auth.ts @@ -0,0 +1,71 @@ +import { AuthConfig, BaseAuth } from '@tachybase/auth'; + +import { namespace, weChatApiOauthBaseUrl, weChatApiOauthScope } from '../constants'; + +export { Model } from '@tachybase/database'; + +export class WeChatAuth extends BaseAuth { + constructor(config: AuthConfig) { + const { ctx } = config; + super({ + ...config, + userCollection: ctx.db.getCollection('users'), + }); + } + + async validate() { + const ctx = this.ctx; + let { code } = ctx.request.query || {}; + code = Array.isArray(code) ? code[0] : code; + + if (!code) { + ctx.logger.error('Failed to get code'); + ctx.throw(401, ctx.t('Unauthorized', { ns: namespace })); + } + + try { + const getAccessTokenRsp = await fetch( + `${weChatApiOauthBaseUrl}access_token?appid=${this.options?.wechatAuth?.AppID}&secret=${this.options?.wechatAuth?.AppSecret}&code=${code}&grant_type=authorization_code`, + ); + const getAccessTokenRspJson = await getAccessTokenRsp.json(); + if (getAccessTokenRspJson.errcode) { + ctx.logger.error(`Failed to get user accessToken: ${getAccessTokenRspJson.errmsg}`); + ctx.throw(401, ctx.t('Failed to get accessToken', { ns: namespace })); + } + const { access_token, unionid } = getAccessTokenRspJson; + const authenticator = this.authenticator; + const user = await authenticator.findUser(unionid); + if (user) { + return user; + } + // Create new user + const { autoSignUp } = this.options?.public || {}; + if (!autoSignUp) { + ctx.logger.error(`User not found: ${unionid}`); + ctx.throw(401, ctx.t('User not found', { ns: namespace })); + } + return await authenticator.newUser(unionid, { + unionid, + }); + } catch (err) { + ctx.logger.error(`Failed in validate: ${err}`); + ctx.throw(401, ctx.t('Unauthorized', { ns: namespace })); + } + } + + async getAuthCfg(redirect) { + const appID = this.options?.wechatAuth?.AppID; + const app = this.ctx.app.name; + const redirectUrl = encodeURIComponent(`${this.ctx.host}${process.env.API_BASE_PATH}wechatAuth:redirect`); + // TODO: 如果后续有登录后绑定的场景,服务端需要校验 state + const state = encodeURIComponent( + encodeURIComponent(`redirect=${redirect}&app=${app}&name=${this.ctx.headers['x-authenticator']}`), + ); + return { + appId: appID, + scope: weChatApiOauthScope, + redirectUrl, + state, + }; + } +} diff --git a/packages/plugins/@tachybase/plugin-wechat-auth/src/swagger/index.ts b/packages/plugins/@tachybase/plugin-wechat-auth/src/swagger/index.ts new file mode 100644 index 000000000..40b6c3d80 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-wechat-auth/src/swagger/index.ts @@ -0,0 +1,157 @@ +const user = { + type: 'object', + description: '用户', + properties: { + id: { + type: 'integer', + description: 'ID', + }, + nickname: { + type: 'string', + description: '昵称', + }, + email: { + type: 'string', + description: '邮箱', + }, + phone: { + type: 'string', + description: '手机号', + }, + appLang: { + type: 'string', + description: '用户使用语言', + }, + systemSettings: { + type: 'object', + description: '系统设置', + properties: { + theme: { + type: 'string', + description: '用户使用主题', + }, + }, + }, + createdAt: { + type: 'string', + format: 'date-time', + description: '创建时间', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: '更新时间', + }, + createdById: { + type: 'integer', + description: '创建人', + }, + updatedById: { + type: 'integer', + description: '更新人', + }, + }, +}; + +export default { + info: { + title: 'TachyBase API - WeChat Auth plugin', + }, + paths: { + '/wechatAuth:getAuthCfg': { + security: [], + get: { + description: 'Get WeChat authorization configuration', + tags: ['WeChat'], + parameters: [ + { + name: 'X-Authenticator', + description: '登录方式标识', + in: 'header', + schema: { + type: 'string', + }, + required: true, + }, + ], + responses: { + 200: { + description: 'ok', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + '/wechatAuth:signIn': { + security: [], + post: { + description: 'WeChat sign in', + tags: ['WeChat'], + parameters: [ + { + name: 'X-Authenticator', + description: '登录方式标识', + in: 'header', + schema: { + type: 'string', + }, + required: true, + }, + { + name: 'tachybase_wechat_auth', + description: 'state校验值', + in: 'cookie', + schema: { + type: 'string', + }, + required: true, + }, + ], + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + code: { + type: 'string', + }, + state: { + type: 'string', + }, + iss: { + type: 'string', + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: 'ok', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + token: { + type: 'string', + }, + user, + }, + }, + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 069550827..06acf596f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,12 +80,18 @@ importers: commander: specifier: ^9.5.0 version: 9.5.0 + eslint: + specifier: ^9.10.0 + version: 9.10.0 eslint-plugin-jest-dom: specifier: ^5.4.0 - version: 5.4.0(@testing-library/dom@10.4.0)(eslint@9.9.0) + version: 5.4.0(@testing-library/dom@10.4.0)(eslint@9.10.0) + eslint-plugin-react: + specifier: ^7.33.0 + version: 7.33.2(eslint@9.10.0) eslint-plugin-testing-library: specifier: ^5.11.1 - version: 5.11.1(eslint@9.9.0)(typescript@5.4.5) + version: 5.11.1(eslint@9.10.0)(typescript@5.4.5) globals: specifier: ^15.9.0 version: 15.9.0 @@ -122,9 +128,12 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + typescript-eslint: + specifier: ^8.4.0 + version: 8.4.0(eslint@9.10.0)(typescript@5.4.5) umi: specifier: ^4.3.3 - version: 4.3.3(@babel/core@7.22.10)(@types/node@20.14.2)(@types/react@18.3.3)(eslint@9.9.0)(lightningcss@1.26.0)(prettier@3.2.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@16.8.2(typescript@5.4.5))(terser@5.31.6)(type-fest@4.25.0)(typescript@5.4.5)(webpack@5.93.0) + version: 4.3.3(@babel/core@7.22.10)(@types/node@20.14.2)(@types/react@18.3.3)(eslint@9.10.0)(lightningcss@1.26.0)(prettier@3.2.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@16.8.2(typescript@5.4.5))(terser@5.31.6)(type-fest@4.25.0)(typescript@5.4.5)(webpack@5.93.0) vitest: specifier: ^1.6.0 version: 1.6.0(@types/node@20.14.2)(jsdom@24.1.1(canvas@2.11.2(encoding@0.1.13)))(less@4.1.3)(lightningcss@1.26.0)(sass@1.77.8)(terser@5.31.6) @@ -4081,6 +4090,43 @@ importers: specifier: ^4.0.525 version: 4.0.759(encoding@0.1.13) + packages/plugins/@tachybase/plugin-wechat-auth: + dependencies: + '@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/database': + specifier: workspace:* + version: link:../../../core/database + '@tachybase/plugin-auth': + specifier: workspace:* + version: link:../plugin-auth + '@tachybase/schema': + specifier: workspace:* + version: link:../../../core/schema + '@tachybase/server': + specifier: workspace:* + version: link:../../../core/server + devDependencies: + '@ant-design/icons': + specifier: ~5.3.7 + version: 5.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd: + specifier: 5.19.4 + version: 5.19.4(date-fns@3.6.0)(luxon@3.5.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-i18next: + specifier: ^15.0.1 + version: 15.0.1(i18next@23.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router-dom: + specifier: ^6.25.1 + version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + packages/plugins/@tachybase/plugin-wechat-official-account: dependencies: '@ant-design/icons': @@ -7220,8 +7266,8 @@ packages: resolution: {integrity: sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-array@0.17.1': - resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} + '@eslint/config-array@0.18.0': + resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@2.1.4': @@ -7240,14 +7286,14 @@ packages: resolution: {integrity: sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.9.0': - resolution: {integrity: sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.4': resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.1.0': + resolution: {integrity: sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ethereumjs/rlp@4.0.1': resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} engines: {node: '>=14'} @@ -9671,6 +9717,17 @@ packages: typescript: optional: true + '@typescript-eslint/eslint-plugin@8.4.0': + resolution: {integrity: sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/parser@5.62.0': resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9691,6 +9748,16 @@ packages: typescript: optional: true + '@typescript-eslint/parser@8.4.0': + resolution: {integrity: sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/scope-manager@5.62.0': resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9699,6 +9766,10 @@ packages: resolution: {integrity: sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==} engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/scope-manager@8.4.0': + resolution: {integrity: sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/type-utils@5.62.0': resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9719,6 +9790,15 @@ packages: typescript: optional: true + '@typescript-eslint/type-utils@8.4.0': + resolution: {integrity: sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/types@5.62.0': resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9727,6 +9807,10 @@ packages: resolution: {integrity: sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==} engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/types@8.4.0': + resolution: {integrity: sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@5.62.0': resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9745,6 +9829,15 @@ packages: typescript: optional: true + '@typescript-eslint/typescript-estree@8.4.0': + resolution: {integrity: sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/utils@5.62.0': resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9757,6 +9850,12 @@ packages: peerDependencies: eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/utils@8.4.0': + resolution: {integrity: sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/visitor-keys@5.62.0': resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9765,6 +9864,10 @@ packages: resolution: {integrity: sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==} engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/visitor-keys@8.4.0': + resolution: {integrity: sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@umijs/ast@4.3.3': resolution: {integrity: sha512-5aw402aLXxGkCZjPJ5vkQpBdfKkcAe+19A5nqCj6AvV+hpUtDsywu9Vy5CCiD1MWpTUEvyWGA4f+hfvoZLTPUQ==} @@ -12824,8 +12927,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true - eslint@9.9.0: - resolution: {integrity: sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==} + eslint@9.10.0: + resolution: {integrity: sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -12856,10 +12959,6 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -15895,10 +15994,6 @@ packages: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} @@ -17323,6 +17418,19 @@ packages: react-native: optional: true + react-i18next@15.0.1: + resolution: {integrity: sha512-NwxLqNM6CLbeGA9xPsjits0EnXdKgCRSS6cgkgOdNcPXqL+1fYNl8fBg1wmnnHvFy812Bt4IWTPE9zjoPmFj3w==} + peerDependencies: + i18next: '>= 23.2.3' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + react-iframe@1.8.5: resolution: {integrity: sha512-F4cQJGs3ydaG6fJWfuz9yLwOU0Trzl6kttXuUG+vYwosH8enOOFxZWEDQCSbNVO8ayjfYZeqLxEvdvcsSy4GvA==} peerDependencies: @@ -18876,6 +18984,12 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + ts-command-line-args@2.5.1: resolution: {integrity: sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==} hasBin: true @@ -19109,6 +19223,15 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript-eslint@8.4.0: + resolution: {integrity: sha512-67qoc3zQZe3CAkO0ua17+7aCLI0dU+sSQd1eKPGq06QE4rfQjstVXR6woHO5qQvGUa550NfGckT4tzh3b3c8Pw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} @@ -21299,11 +21422,11 @@ snapshots: eslint-visitor-keys: 2.1.0 semver: 6.3.1 - '@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@9.9.0)': + '@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@9.10.0)': dependencies: '@babel/core': 7.23.6 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 9.9.0 + eslint: 9.10.0 eslint-visitor-keys: 2.1.0 semver: 6.3.1 @@ -23159,16 +23282,16 @@ snapshots: eslint: 8.55.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.0(eslint@9.9.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.10.0)': dependencies: - eslint: 9.9.0 + eslint: 9.10.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.11.0': {} '@eslint/compat@1.1.1': {} - '@eslint/config-array@0.17.1': + '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 debug: 4.3.6(supports-color@8.1.1) @@ -23208,10 +23331,12 @@ snapshots: '@eslint/js@9.10.0': {} - '@eslint/js@9.9.0': {} - '@eslint/object-schema@2.1.4': {} + '@eslint/plugin-kit@0.1.0': + dependencies: + levn: 0.4.1 + '@ethereumjs/rlp@4.0.1': {} '@ethereumjs/rlp@5.0.2': {} @@ -24592,7 +24717,7 @@ snapshots: async: 3.2.5 chalk: 3.0.0 dayjs: 1.11.10 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.6(supports-color@8.1.1) eventemitter2: 5.0.1 fast-json-patch: 3.1.1 fclone: 1.0.11 @@ -24612,7 +24737,7 @@ snapshots: '@opencensus/core': 0.0.9 '@opencensus/propagation-b3': 0.0.8 async: 2.6.4 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.6(supports-color@8.1.1) eventemitter2: 6.4.9 require-in-the-middle: 5.2.0 semver: 7.5.4 @@ -24625,9 +24750,9 @@ snapshots: '@pm2/js-api@0.8.0': dependencies: async: 2.6.4 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.6(supports-color@8.1.1) eventemitter2: 6.4.9 - extrareqp2: 1.0.0(debug@4.3.5) + extrareqp2: 1.0.0(debug@4.3.6) ws: 7.5.9 transitivePeerDependencies: - bufferutil @@ -26443,15 +26568,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 5.62.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/parser': 5.62.0(eslint@9.10.0)(typescript@5.4.5) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@9.9.0)(typescript@5.4.5) - '@typescript-eslint/utils': 5.62.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 5.62.0(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/utils': 5.62.0(eslint@9.10.0)(typescript@5.4.5) debug: 4.3.6(supports-color@8.1.1) - eslint: 9.9.0 + eslint: 9.10.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare-lite: 1.4.0 @@ -26482,6 +26607,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.4.0(@typescript-eslint/parser@8.4.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 8.4.0(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 8.4.0 + '@typescript-eslint/type-utils': 8.4.0(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.4.0(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.4.0 + eslint: 9.10.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.4.4)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 @@ -26494,13 +26637,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/parser@5.62.0(eslint@9.10.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) debug: 4.3.6(supports-color@8.1.1) - eslint: 9.9.0 + eslint: 9.10.0 optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: @@ -26519,6 +26662,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.4.0(eslint@9.10.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/scope-manager': 8.4.0 + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/typescript-estree': 8.4.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.4.0 + debug: 4.3.6(supports-color@8.1.1) + eslint: 9.10.0 + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 @@ -26529,6 +26685,11 @@ snapshots: '@typescript-eslint/types': 6.14.0 '@typescript-eslint/visitor-keys': 6.14.0 + '@typescript-eslint/scope-manager@8.4.0': + dependencies: + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/visitor-keys': 8.4.0 + '@typescript-eslint/type-utils@5.62.0(eslint@8.55.0)(typescript@5.4.4)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.4) @@ -26541,12 +26702,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@5.62.0(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@5.62.0(eslint@9.10.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - '@typescript-eslint/utils': 5.62.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/utils': 5.62.0(eslint@9.10.0)(typescript@5.4.5) debug: 4.3.6(supports-color@8.1.1) - eslint: 9.9.0 + eslint: 9.10.0 tsutils: 3.21.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 @@ -26565,10 +26726,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.4.0(eslint@9.10.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/typescript-estree': 8.4.0(typescript@5.4.5) + '@typescript-eslint/utils': 8.4.0(eslint@9.10.0)(typescript@5.4.5) + debug: 4.3.6(supports-color@8.1.1) + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - eslint + - supports-color + '@typescript-eslint/types@5.62.0': {} '@typescript-eslint/types@6.14.0': {} + '@typescript-eslint/types@8.4.0': {} + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.4)': dependencies: '@typescript-eslint/types': 5.62.0 @@ -26604,13 +26779,28 @@ snapshots: debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.2 + semver: 7.6.3 ts-api-utils: 1.0.3(typescript@5.4.4) optionalDependencies: typescript: 5.4.4 transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.4.0(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/visitor-keys': 8.4.0 + debug: 4.3.6(supports-color@8.1.1) + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@5.62.0(eslint@8.55.0)(typescript@5.4.4)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) @@ -26626,15 +26816,15 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@5.62.0(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/utils@5.62.0(eslint@9.10.0)(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - eslint: 9.9.0 + eslint: 9.10.0 eslint-scope: 5.1.1 semver: 7.6.3 transitivePeerDependencies: @@ -26650,7 +26840,18 @@ snapshots: '@typescript-eslint/types': 6.14.0 '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.4.4) eslint: 8.55.0 - semver: 7.6.2 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.4.0(eslint@9.10.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0) + '@typescript-eslint/scope-manager': 8.4.0 + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/typescript-estree': 8.4.0(typescript@5.4.5) + eslint: 9.10.0 transitivePeerDependencies: - supports-color - typescript @@ -26665,6 +26866,11 @@ snapshots: '@typescript-eslint/types': 6.14.0 eslint-visitor-keys: 3.4.3 + '@typescript-eslint/visitor-keys@8.4.0': + dependencies: + '@typescript-eslint/types': 8.4.0 + eslint-visitor-keys: 3.4.3 + '@umijs/ast@4.3.3': dependencies: '@umijs/bundler-utils': 4.3.3 @@ -26899,17 +27105,17 @@ snapshots: - supports-color - typescript - '@umijs/lint@4.3.3(eslint@9.9.0)(stylelint@16.8.2(typescript@5.4.5))(typescript@5.4.5)': + '@umijs/lint@4.3.3(eslint@9.10.0)(stylelint@16.8.2(typescript@5.4.5))(typescript@5.4.5)': dependencies: '@babel/core': 7.23.6 - '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@9.9.0) + '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@9.10.0) '@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2(postcss@8.4.39))(postcss@8.4.39) - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5) - '@typescript-eslint/parser': 5.62.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/parser': 5.62.0(eslint@9.10.0)(typescript@5.4.5) '@umijs/babel-preset-umi': 4.3.3 - eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5) - eslint-plugin-react: 7.33.2(eslint@9.9.0) - eslint-plugin-react-hooks: 4.6.0(eslint@9.9.0) + eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5) + eslint-plugin-react: 7.33.2(eslint@9.10.0) + eslint-plugin-react-hooks: 4.6.0(eslint@9.10.0) postcss: 8.4.39 postcss-syntax: 0.36.2(postcss@8.4.39) stylelint-config-standard: 25.0.0(stylelint@16.8.2(typescript@5.4.5)) @@ -30419,10 +30625,10 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest-dom@5.4.0(@testing-library/dom@10.4.0)(eslint@9.9.0): + eslint-plugin-jest-dom@5.4.0(@testing-library/dom@10.4.0)(eslint@9.10.0): dependencies: '@babel/runtime': 7.25.0 - eslint: 9.9.0 + eslint: 9.10.0 requireindex: 1.2.0 optionalDependencies: '@testing-library/dom': 10.4.0 @@ -30437,12 +30643,12 @@ snapshots: - supports-color - typescript - eslint-plugin-jest@27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5): + eslint-plugin-jest@27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5): dependencies: - '@typescript-eslint/utils': 5.62.0(eslint@9.9.0)(typescript@5.4.5) - eslint: 9.9.0 + '@typescript-eslint/utils': 5.62.0(eslint@9.10.0)(typescript@5.4.5) + eslint: 9.10.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.0)(typescript@5.4.5))(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5) transitivePeerDependencies: - supports-color - typescript @@ -30482,9 +30688,9 @@ snapshots: dependencies: eslint: 8.55.0 - eslint-plugin-react-hooks@4.6.0(eslint@9.9.0): + eslint-plugin-react-hooks@4.6.0(eslint@9.10.0): dependencies: - eslint: 9.9.0 + eslint: 9.10.0 eslint-plugin-react@7.33.2(eslint@8.55.0): dependencies: @@ -30506,14 +30712,14 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.11 - eslint-plugin-react@7.33.2(eslint@9.9.0): + eslint-plugin-react@7.33.2(eslint@9.10.0): dependencies: array-includes: 3.1.8 array.prototype.flatmap: 1.3.2 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.0.19 - eslint: 9.9.0 + eslint: 9.10.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 @@ -30526,10 +30732,10 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.11 - eslint-plugin-testing-library@5.11.1(eslint@9.9.0)(typescript@5.4.5): + eslint-plugin-testing-library@5.11.1(eslint@9.10.0)(typescript@5.4.5): dependencies: - '@typescript-eslint/utils': 5.62.0(eslint@9.9.0)(typescript@5.4.5) - eslint: 9.9.0 + '@typescript-eslint/utils': 5.62.0(eslint@9.10.0)(typescript@5.4.5) + eslint: 9.10.0 transitivePeerDependencies: - supports-color - typescript @@ -30604,13 +30810,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@9.9.0: + eslint@9.10.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0) '@eslint-community/regexpp': 4.11.0 - '@eslint/config-array': 0.17.1 + '@eslint/config-array': 0.18.0 '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.9.0 + '@eslint/js': 9.10.0 + '@eslint/plugin-kit': 0.1.0 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 @@ -30622,7 +30829,7 @@ snapshots: eslint-scope: 8.0.2 eslint-visitor-keys: 4.0.0 espree: 10.1.0 - esquery: 1.6.0 + esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -30633,11 +30840,10 @@ snapshots: is-glob: 4.0.3 is-path-inside: 3.0.3 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.4 + optionator: 0.9.3 strip-ansi: 6.0.1 text-table: 0.2.0 transitivePeerDependencies: @@ -30663,10 +30869,6 @@ snapshots: dependencies: estraverse: 5.3.0 - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -30928,9 +31130,9 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 - extrareqp2@1.0.0(debug@4.3.5): + extrareqp2@1.0.0(debug@4.3.6): dependencies: - follow-redirects: 1.15.6(debug@4.3.5) + follow-redirects: 1.15.6(debug@4.3.6) transitivePeerDependencies: - debug @@ -31145,6 +31347,10 @@ snapshots: optionalDependencies: debug: 4.3.5(supports-color@5.5.0) + follow-redirects@1.15.6(debug@4.3.6): + optionalDependencies: + debug: 4.3.6(supports-color@8.1.1) + fontkit@2.0.2: dependencies: '@swc/helpers': 0.4.36 @@ -34302,15 +34508,6 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - ora@5.4.1: dependencies: bl: 4.1.0 @@ -34707,7 +34904,7 @@ snapshots: pm2-axon-rpc@0.7.1: dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -34715,7 +34912,7 @@ snapshots: dependencies: amp: 0.3.1 amp-message: 0.1.2 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.6(supports-color@8.1.1) escape-string-regexp: 4.0.0 transitivePeerDependencies: - supports-color @@ -34732,7 +34929,7 @@ snapshots: pm2-sysmonit@1.2.8: dependencies: async: 3.2.5 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.6(supports-color@8.1.1) pidusage: 2.0.21 systeminformation: 5.22.8 tx2: 1.0.5 @@ -35936,6 +36133,15 @@ snapshots: optionalDependencies: react-dom: 18.3.1(react@18.3.1) + react-i18next@15.0.1(i18next@23.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.0 + html-parse-stringify: 3.0.1 + i18next: 23.13.0 + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-iframe@1.8.5(react@18.3.1): dependencies: object-assign: 4.1.1 @@ -37840,6 +38046,10 @@ snapshots: dependencies: typescript: 5.4.4 + ts-api-utils@1.3.0(typescript@5.4.5): + dependencies: + typescript: 5.4.5 + ts-command-line-args@2.5.1: dependencies: chalk: 4.1.2 @@ -38150,6 +38360,17 @@ snapshots: typedarray@0.0.6: {} + typescript-eslint@8.4.0(eslint@9.10.0)(typescript@5.4.5): + dependencies: + '@typescript-eslint/eslint-plugin': 8.4.0(@typescript-eslint/parser@8.4.0(eslint@9.10.0)(typescript@5.4.5))(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/parser': 8.4.0(eslint@9.10.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.4.0(eslint@9.10.0)(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - eslint + - supports-color + typescript@4.9.5: {} typescript@5.4.4: {} @@ -38179,13 +38400,13 @@ snapshots: uglify-to-browserify@1.0.2: optional: true - umi@4.3.3(@babel/core@7.22.10)(@types/node@20.14.2)(@types/react@18.3.3)(eslint@9.9.0)(lightningcss@1.26.0)(prettier@3.2.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@16.8.2(typescript@5.4.5))(terser@5.31.6)(type-fest@4.25.0)(typescript@5.4.5)(webpack@5.93.0): + umi@4.3.3(@babel/core@7.22.10)(@types/node@20.14.2)(@types/react@18.3.3)(eslint@9.10.0)(lightningcss@1.26.0)(prettier@3.2.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@16.8.2(typescript@5.4.5))(terser@5.31.6)(type-fest@4.25.0)(typescript@5.4.5)(webpack@5.93.0): dependencies: '@babel/runtime': 7.23.6 '@umijs/bundler-utils': 4.3.3 '@umijs/bundler-webpack': 4.3.3(type-fest@4.25.0)(typescript@5.4.5)(webpack@5.93.0) '@umijs/core': 4.3.3 - '@umijs/lint': 4.3.3(eslint@9.9.0)(stylelint@16.8.2(typescript@5.4.5))(typescript@5.4.5) + '@umijs/lint': 4.3.3(eslint@9.10.0)(stylelint@16.8.2(typescript@5.4.5))(typescript@5.4.5) '@umijs/preset-umi': 4.3.3(@types/node@20.14.2)(@types/react@18.3.3)(lightningcss@1.26.0)(rollup@3.29.4)(sass@1.77.8)(terser@5.31.6)(type-fest@4.25.0)(typescript@5.4.5)(webpack@5.93.0) '@umijs/renderer-react': 4.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@umijs/server': 4.3.3