From 449845315e0042eec9443c0f853ee12c99441bcb Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Tue, 27 Jun 2023 15:00:42 +0800 Subject: [PATCH] feat(admin): add system notify --- pnpm-lock.yaml | 18 +-- server/admin/package.json | 2 +- server/admin/src/client/App.tsx | 6 + server/admin/src/client/i18n.ts | 26 ++++ .../admin/src/client/routes/system/notify.tsx | 141 ++++++++++++++++++ server/admin/src/server/router/api.ts | 34 +++++ 6 files changed, 217 insertions(+), 10 deletions(-) create mode 100644 server/admin/src/client/routes/system/notify.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a36cf044..1d0faac2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -574,7 +574,7 @@ importers: version: 0.32.11 zustand: specifier: ^4.3.6 - version: 4.3.6(immer@9.0.21)(react@18.2.0) + version: 4.3.6(immer@9.0.15)(react@18.2.0) devDependencies: '@types/crc': specifier: ^3.4.0 @@ -1556,8 +1556,8 @@ importers: specifier: workspace:^ version: link:../packages/sdk tushan: - specifier: ^0.2.28 - version: 0.2.28(history@5.3.0)(prop-types@15.8.1)(react-hook-form@7.41.5)(ts-node@10.9.1) + specifier: ^0.2.32 + version: 0.2.32(history@5.3.0)(prop-types@15.8.1)(react-hook-form@7.41.5)(ts-node@10.9.1) vite-express: specifier: 0.8.0 version: 0.8.0(patch_hash=u6touqej4dt3zxnslnszarl7vq)(express@4.18.2)(vite@4.2.0) @@ -1875,7 +1875,7 @@ importers: version: 5.3.6(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) zustand: specifier: ^4.3.6 - version: 4.3.6(immer@9.0.21)(react@18.2.0) + version: 4.3.6(immer@9.0.15)(react@18.2.0) server/plugins/com.msgbyte.getui: dependencies: @@ -2053,7 +2053,7 @@ importers: version: 5.3.6(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) zustand: specifier: ^4.3.6 - version: 4.3.6(immer@9.0.21)(react@18.2.0) + version: 4.3.6(immer@9.0.15)(react@18.2.0) server/plugins/com.msgbyte.welcome: dependencies: @@ -21749,10 +21749,10 @@ packages: /immer@9.0.15: resolution: {integrity: sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==} - dev: false /immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + dev: false /import-fresh@2.0.0: resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} @@ -34259,8 +34259,8 @@ packages: domino: 2.1.6 dev: false - /tushan@0.2.28(history@5.3.0)(prop-types@15.8.1)(react-hook-form@7.41.5)(ts-node@10.9.1): - resolution: {integrity: sha512-8lYhCgOqvayRHkC6oT44WRzGWIjf0Eh565sjMLmyS5+9K10s/Wb0W1z8mPdx8N+PLDMhA2sBchc9Q3cLjJtA6g==} + /tushan@0.2.32(history@5.3.0)(prop-types@15.8.1)(react-hook-form@7.41.5)(ts-node@10.9.1): + resolution: {integrity: sha512-yTv1TTUrCSJqzaYxJ1T21pjbxlQnyiEx2bAjB4VDixhrDFMwRugBpy+bZ58imKq1NKv2SZLi8NQlIUW8x432Eg==} dependencies: '@arco-design/web-react': 2.49.2(@types/react@18.0.20)(react-dom@18.2.0)(react@18.2.0) '@tanstack/react-query': 4.29.3(react-dom@18.2.0)(react@18.2.0) @@ -36412,7 +36412,6 @@ packages: immer: 9.0.15 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) - dev: false /zustand@4.3.6(immer@9.0.21)(react@18.2.0): resolution: {integrity: sha512-6J5zDxjxLE+yukC2XZWf/IyWVKnXT9b9HUv09VJ/bwGCpKNcaTqp7Ws28Xr8jnbvnZcdRaidztAPsXFBIqufiw==} @@ -36429,6 +36428,7 @@ packages: immer: 9.0.21 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) + dev: false /zwitch@1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} diff --git a/server/admin/package.json b/server/admin/package.json index 847934e3..d93ec36d 100644 --- a/server/admin/package.json +++ b/server/admin/package.json @@ -29,7 +29,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "tailchat-server-sdk": "workspace:^", - "tushan": "^0.2.28", + "tushan": "^0.2.32", "vite-express": "0.8.0" }, "devDependencies": { diff --git a/server/admin/src/client/App.tsx b/server/admin/src/client/App.tsx index e07938c0..ef394dbb 100644 --- a/server/admin/src/client/App.tsx +++ b/server/admin/src/client/App.tsx @@ -11,6 +11,7 @@ import { IconEmail, IconFile, IconMessage, + IconNotification, IconSettings, IconStorage, IconUser, @@ -27,6 +28,7 @@ import { CacheManager } from './routes/cache'; import { TailchatNetwork } from './routes/network'; import { SocketIOAdmin } from './routes/socketio'; import { SystemConfig } from './routes/system'; +import { SystemNotify } from './routes/system/notify'; const dataProvider = jsonServerProvider('/admin/api', httpClient); @@ -111,6 +113,10 @@ function App() { + }> + + + }> diff --git a/server/admin/src/client/i18n.ts b/server/admin/src/client/i18n.ts index 759bbf3d..4b4e08a2 100644 --- a/server/admin/src/client/i18n.ts +++ b/server/admin/src/client/i18n.ts @@ -61,6 +61,18 @@ export const i18n: TushanContextProps['i18n'] = { 'Please be cautious in the production environment, clearing the cache may lead to increased pressure on the database in a short period of time', cleanBtn: 'Clean Cache', }, + 'system-notify': { + create: 'Create System Notify', + title: 'Title', + content: 'Content', + scope: 'Notify Scope', + allUser: 'All User', + allUserTip: + 'All users excluding temporary users. Also, if there are many users, it may not be possible to notify all users at once', + specifiedUser: 'Specified User', + notifySuccess: + 'Sent successfully, sent to ${data.userIds.length} users', + }, }, }, }, @@ -153,6 +165,9 @@ export const i18n: TushanContextProps['i18n'] = { cache: { name: '缓存管理', }, + 'system-notify': { + name: '系统通知', + }, }, custom: { action: { @@ -204,6 +219,17 @@ export const i18n: TushanContextProps['i18n'] = { '生产环境请谨慎操作, 清理缓存可能会导致短时间内数据库压力增加', cleanBtn: '清理缓存', }, + 'system-notify': { + create: '创建系统通知', + title: '标题', + content: '内容', + scope: '通知范围', + allUser: '所有用户', + allUserTip: + '所有用户不包含临时用户。另外,如果用户很多,可能会无法立即通知所有用户', + specifiedUser: '指定用户', + notifySuccess: '发送成功,已发送给 ${count} 名用户', + }, }, }, }, diff --git a/server/admin/src/client/routes/system/notify.tsx b/server/admin/src/client/routes/system/notify.tsx new file mode 100644 index 00000000..c1858d01 --- /dev/null +++ b/server/admin/src/client/routes/system/notify.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { + Button, + Input, + Form, + useTranslation, + Typography, + Card, + Radio, + ReferenceFieldEdit, + useAsyncRequest, + Tooltip, + Message, +} from 'tushan'; +import { IconExclamationCircle } from 'tushan/icon'; +import { MarkdownEditor } from '../../components/MarkdownEditor'; +import { request } from '../../request'; + +/** + * Tailchat 系统通知 + * + * 发送markdown格式的消息到指定用户的收件箱 + */ +export const SystemNotify: React.FC = React.memo(() => { + const { t } = useTranslation(); + const [form] = Form.useForm(); + const scope: 'all' | 'specified' = Form.useWatch('scope', form); + + const [{ loading }, handleSubmit] = useAsyncRequest(async (values) => { + const { data } = await request.post('/users/system/notify', { + scope: values.scope, + specifiedUser: values.specifiedUser, + title: values.title, + content: values.content, + }); + + Message.success( + t('custom.system-notify.notifySuccess', { count: data.userIds.length }) + ); + }); + + return ( + + + {t('custom.system-notify.create')} + + +
+ + + + + + + + + + + + {t('custom.system-notify.allUser')} + + + + + + + {t('custom.system-notify.specifiedUser')} + + + + + {scope === 'specified' && ( + + + + )} + + + + +
+
+ ); +}); +SystemNotify.displayName = 'SystemNotify'; + +export const MarkdownFormInput: React.FC<{ + value?: string; + onChange?: (val: string) => void; +}> = React.memo((props) => { + const value = props.value || ''; + + const handleChange = (newValue) => { + props.onChange && props.onChange(newValue); + }; + + return ; +}); +MarkdownFormInput.displayName = 'MarkdownFormInput'; + +export const UserSelectedFormInput: React.FC<{ + value?: string; + onChange?: (val: string) => void; +}> = React.memo((props) => { + const value = props.value || ''; + + const handleChange = (newValue) => { + props.onChange && props.onChange(newValue); + }; + + /** + * Wait for ReferenceMany + */ + return ( + + ); +}); +UserSelectedFormInput.displayName = 'UserSelectedFormInput'; diff --git a/server/admin/src/server/router/api.ts b/server/admin/src/server/router/api.ts index c276cd62..9bd43639 100644 --- a/server/admin/src/server/router/api.ts +++ b/server/admin/src/server/router/api.ts @@ -121,6 +121,40 @@ router.post('/user/unban', auth(), async (req, res) => { ret, }); }); +router.post('/users/system/notify', auth(), async (req, res) => { + const { scope, specifiedUser, title, content } = req.body; + + let userIds = []; + + if (scope === 'all') { + const users = await userModel.find( + { + // false 或 null(正式用户或者老的用户) + temporary: { + $ne: true, + }, + }, + { + _id: 1, + } + ); + + userIds = users.map((u) => u._id); + } else if (scope === 'specified') { + userIds = Array.isArray(specifiedUser) ? specifiedUser : [specifiedUser]; + } + + broker.call('chat.inbox.batchAppend', { + userIds, + type: 'markdown', + payload: { + title, + content, + }, + }); + + res.json({ userIds }); +}); router.use( '/users', auth(),