From 8b2e6d1adb2aa3fcbf0501549bc10d9870596230 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Fri, 24 Jun 2022 21:49:25 +0800 Subject: [PATCH 01/27] client: fix dark mode text --- packages/client/src/components/wiki/tocs/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/wiki/tocs/index.tsx b/packages/client/src/components/wiki/tocs/index.tsx index 8d60e4a..5229c52 100644 --- a/packages/client/src/components/wiki/tocs/index.tsx +++ b/packages/client/src/components/wiki/tocs/index.tsx @@ -179,7 +179,7 @@ export const WikiTocs: React.FC = ({ > - 主页 + 主页 @@ -220,7 +220,7 @@ export const WikiTocs: React.FC = ({ > - 设置 + 设置 From 6cde76130edeb23bc11dd6967431a8cc7e462fe1 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sat, 25 Jun 2022 11:05:21 +0800 Subject: [PATCH 02/27] tiptap: fix title class --- packages/client/src/tiptap/core/extensions/title.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/tiptap/core/extensions/title.ts b/packages/client/src/tiptap/core/extensions/title.ts index 1e47cf0..b812c2a 100644 --- a/packages/client/src/tiptap/core/extensions/title.ts +++ b/packages/client/src/tiptap/core/extensions/title.ts @@ -30,7 +30,7 @@ export const Title = Node.create({ addOptions() { return { HTMLAttributes: { - class: 'title', + class: 'node-title', }, }; }, @@ -47,7 +47,7 @@ export const Title = Node.create({ parseHTML() { return [ { - tag: 'div[class=title]', + tag: 'div[class=node-title]', }, ]; }, From dbd77d887f17842b2637b9c8da8fafdcadac3148 Mon Sep 17 00:00:00 2001 From: jonny <475634114@qq.com> Date: Sun, 26 Jun 2022 12:07:37 +0800 Subject: [PATCH 03/27] Optimization docker-compose description --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 765ccee..b5541e5 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,14 @@ docker run --name think-redis -p 6379:6379 -d redis --appendonly yes --requirepa - 实测腾讯轻量云 2C4G 机器构建需 8 分钟左右 -**请注意修改 `docker-compose.yml` 中的 `EIP` 参数,否则无法正常使用!!!** +**请注意构建前修改 `docker-compose.yml` 中的 `EIP` 参数,否则无法正常使用!!!** ``` # 首次安装 git clone https://github.com/fantasticit/think.git cd think +vim docker-compose.yml docker-compose up -d # 二次更新升级 From df08e7be402965973a49b821855da7f65de02824 Mon Sep 17 00:00:00 2001 From: jonny <475634114@qq.com> Date: Sun, 26 Jun 2022 17:26:57 +0800 Subject: [PATCH 04/27] Add caddy2 configuration file example --- README.md | 69 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b5541e5..e9d9d27 100644 --- a/README.md +++ b/README.md @@ -49,24 +49,6 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 依赖安装命令: `npm i -g pm2 @nestjs/cli pnpm` -#### 数据库 - -首先安装 `MySQL`,推荐使用 docker 进行安装。 - -```bash -docker image pull mysql:5.7 -# m1 的 mac 可以用:docker image pull --platform linux/x86_64 mysql:5.7 -docker run -d --restart=always --name think -p 3306:3306 -e MYSQL_DATABASE=think -e MYSQL_ROOT_PASSWORD=root mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci -``` - -#### 可选:Redis - -如果需要文档版本服务,请在根目录 `yaml` 配置中进行 `db.redis` 的配置。 - -``` -docker pull redis:latest -docker run --name think-redis -p 6379:6379 -d redis --appendonly yes --requirepass "root" -``` ## Docker-compose 一键构建安装 @@ -74,7 +56,6 @@ docker run --name think-redis -p 6379:6379 -d redis --appendonly yes --requirepa **请注意构建前修改 `docker-compose.yml` 中的 `EIP` 参数,否则无法正常使用!!!** - ``` # 首次安装 git clone https://github.com/fantasticit/think.git @@ -105,6 +86,26 @@ docker-compose up -d 如需修改配置,开发环境编辑 `config/dev.yaml`。生产环境编辑 `config/prod.yaml` (如没有,可复制开发环境的配置修改即可.) +#### 数据库 + +首先安装 `MySQL`,推荐使用 docker 进行安装。 + +```bash +docker image pull mysql:5.7 +# m1 的 mac 可以用:docker image pull --platform linux/x86_64 mysql:5.7 +docker run -d --restart=always --name think -p 3306:3306 -e MYSQL_DATABASE=think -e MYSQL_ROOT_PASSWORD=root mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci +``` + +#### 可选:Redis + +如果需要文档版本服务,请在配置文件中修改 `db.redis` 的配置。 + +``` +docker pull redis:latest +docker run --name think-redis -p 6379:6379 -d redis --appendonly yes --requirepass "root" +``` + + ### 本地源代码运行(开发环境) @@ -136,11 +137,11 @@ pm2 save ### nginx 配置参考 -采用反向代理进行 `nginx` 配置,**同时设置 `proxy_set_header X-Real-IP $remote_addr;` 以便服务端获取到真实 ip 地址**。 +采用 `nginx` 作为反向代理的配置参考(部分),完整版请见 <[think/nginx.conf.bak](https://github.com/fantasticit/think/blob/main/nginx.conf.bak)> ```bash upstream wipi_client { - server 127.0.0.1:3000; + server 127.0.0.1:5001; keepalive 64; } @@ -165,12 +166,36 @@ server { proxy_set_header X-Nginx-Proxy true; proxy_cache_bypass $http_upgrade; proxy_pass http://wipi_client; #反向代理 - proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Real-IP $remote_addr; #获取客户端真实IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` +### caddy2 配置参考 + +采用 caddy v2 作为反向代理的配置文件参考 + +``` +# 例子中的域名,请自行替换. +think.mrdoc.fun { + encode zstd gzip + reverse_proxy localhost:5001 +} + + +thinkapi.mrdoc.fun { + @websockets { + header Connection *Upgrade* + header Upgrade websocket + #path /think/wss/* +} + encode zstd gzip + reverse_proxy /api/* localhost:5002 + reverse_proxy @websockets localhost:5003 +} +``` + ## 自动化部署 > 思路:在服务器部署 webhook,然后在 github setting 中配置相应钩子,实现自动化部署 From 6ca4461496fab7c8e6a5c24443cabbbadc0a408c Mon Sep 17 00:00:00 2001 From: jonny <475634114@qq.com> Date: Sun, 26 Jun 2022 17:29:26 +0800 Subject: [PATCH 05/27] Add example of caddy2 configuration file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9d9d27..a91bb2e 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ docker-compose up -d 如需修改配置,开发环境编辑 `config/dev.yaml`。生产环境编辑 `config/prod.yaml` (如没有,可复制开发环境的配置修改即可.) -#### 数据库 +- 数据库 首先安装 `MySQL`,推荐使用 docker 进行安装。 @@ -96,7 +96,7 @@ docker image pull mysql:5.7 docker run -d --restart=always --name think -p 3306:3306 -e MYSQL_DATABASE=think -e MYSQL_ROOT_PASSWORD=root mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci ``` -#### 可选:Redis +- 可选:Redis 如果需要文档版本服务,请在配置文件中修改 `db.redis` 的配置。 From 48064039039ba2e056a39bb3801b3c2eca25b3fd Mon Sep 17 00:00:00 2001 From: jonny <475634114@qq.com> Date: Sun, 26 Jun 2022 17:34:31 +0800 Subject: [PATCH 06/27] Adapt to the new configuration file --- nginx.conf.bak | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nginx.conf.bak b/nginx.conf.bak index f3e36d9..fca91b5 100644 --- a/nginx.conf.bak +++ b/nginx.conf.bak @@ -1,9 +1,9 @@ -upstream think_server { +upstream think_client { server 127.0.0.1:5001; keepalive 64; } -upstream think_client { +upstream think_server { server 127.0.0.1:5002; keepalive 64; } @@ -29,7 +29,7 @@ server { client_max_body_size 100m; - location /think { + location /api { proxy_pass http://think_server; proxy_read_timeout 300s; proxy_send_timeout 300s; From a164649c163afbfd2d274e359e064ca35be204b6 Mon Sep 17 00:00:00 2001 From: jonny <475634114@qq.com> Date: Sun, 26 Jun 2022 17:38:56 +0800 Subject: [PATCH 07/27] Consistent with the reverse proxy example. --- docker/prod-sample.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/prod-sample.yaml b/docker/prod-sample.yaml index 3b445e5..27cae96 100644 --- a/docker/prod-sample.yaml +++ b/docker/prod-sample.yaml @@ -3,7 +3,7 @@ client: port: 5001 assetPrefix: '/' apiUrl: 'http://localhost:5002/api' - collaborationUrl: 'ws://localhost:5003' + collaborationUrl: 'ws://localhost:5003/think/wss' # 以下为页面 meta 配置 seoAppName: '云策文档' seoDescription: '云策文档是一款开源知识管理工具。通过独立的知识库空间,结构化地组织在线协作文档,实现知识的积累与沉淀,促进知识的复用与流通。' From d5f5a59517a18c1cd604f6687ddf72366b0c4207 Mon Sep 17 00:00:00 2001 From: jonny <475634114@qq.com> Date: Sun, 26 Jun 2022 17:40:27 +0800 Subject: [PATCH 08/27] Keep caddy2 consistent with nginx example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a91bb2e..33e13a4 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ thinkapi.mrdoc.fun { @websockets { header Connection *Upgrade* header Upgrade websocket - #path /think/wss/* + path /think/wss/* } encode zstd gzip reverse_proxy /api/* localhost:5002 From 43cfc2e3390a0a9493b13aa37302f02ccd900d71 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 17:11:26 +0800 Subject: [PATCH 09/27] feat: add admin --- config/dev.yaml | 9 + .../components/admin/system-config/index.tsx | 29 + .../admin/system-config/mail/index.tsx | 88 +++ .../admin/system-config/system/index.tsx | 49 ++ .../document/collaboration/index.tsx | 8 +- packages/client/src/components/user/index.tsx | 12 +- .../src/components/user/setting/index.tsx | 8 +- .../client/src/components/wiki/card/index.tsx | 4 +- .../src/components/wiki/pin-card/index.tsx | 6 +- .../client/src/components/wiki/tocs/index.tsx | 96 +-- packages/client/src/data/user.ts | 98 ++- packages/client/src/hooks/use-interval.tsx | 34 ++ .../hooks/user-isomorphic-layout-effect.tsx | 3 + .../client/src/illustrations/forbidden.tsx | 225 +++++++ .../src/layouts/router-header/index.tsx | 2 +- .../client/src/pages/admin/index.module.scss | 12 + packages/client/src/pages/admin/index.tsx | 55 ++ .../pages/forgetPassword/index.module.scss | 35 ++ .../client/src/pages/forgetPassword/index.tsx | 170 ++++++ packages/client/src/pages/login/index.tsx | 38 +- packages/client/src/pages/register/index.tsx | 113 +++- packages/domains/lib/api/user.d.ts | 48 ++ packages/domains/lib/api/user.js | 48 ++ packages/domains/lib/models/index.d.ts | 1 + packages/domains/lib/models/index.js | 1 + packages/domains/lib/models/system.d.ts | 7 + packages/domains/lib/models/system.js | 2 + packages/domains/lib/models/user.d.ts | 1 + packages/domains/src/api/user.ts | 54 ++ packages/domains/src/models/index.ts | 1 + packages/domains/src/models/system.ts | 7 + packages/domains/src/models/user.ts | 1 + packages/server/package.json | 2 + packages/server/src/app.module.ts | 8 + .../src/controllers/system.controller.ts | 7 + .../server/src/controllers/user.controller.ts | 59 +- .../src/controllers/verify.controller.ts | 23 + packages/server/src/dtos/create-user.dto.ts | 60 +- packages/server/src/dtos/login-user.dto.ts | 2 +- packages/server/src/entities/system.entity.ts | 56 ++ packages/server/src/entities/user.entity.ts | 7 +- packages/server/src/entities/verify.entity.ts | 27 + packages/server/src/modules/system.module.ts | 13 + packages/server/src/modules/user.module.ts | 5 + packages/server/src/modules/verify.module.ts | 14 + .../server/src/services/system.service.ts | 123 ++++ packages/server/src/services/user.service.ts | 171 +++++- .../server/src/services/verify.service.ts | 69 +++ packages/server/src/services/wiki.service.ts | 4 + pnpm-lock.yaml | 574 ++++++++---------- 50 files changed, 2083 insertions(+), 406 deletions(-) create mode 100644 packages/client/src/components/admin/system-config/index.tsx create mode 100644 packages/client/src/components/admin/system-config/mail/index.tsx create mode 100644 packages/client/src/components/admin/system-config/system/index.tsx create mode 100644 packages/client/src/hooks/use-interval.tsx create mode 100644 packages/client/src/hooks/user-isomorphic-layout-effect.tsx create mode 100644 packages/client/src/illustrations/forbidden.tsx create mode 100644 packages/client/src/pages/admin/index.module.scss create mode 100644 packages/client/src/pages/admin/index.tsx create mode 100644 packages/client/src/pages/forgetPassword/index.module.scss create mode 100644 packages/client/src/pages/forgetPassword/index.tsx create mode 100644 packages/domains/lib/models/system.d.ts create mode 100644 packages/domains/lib/models/system.js create mode 100644 packages/domains/src/models/system.ts create mode 100644 packages/server/src/controllers/system.controller.ts create mode 100644 packages/server/src/controllers/verify.controller.ts create mode 100644 packages/server/src/entities/system.entity.ts create mode 100644 packages/server/src/entities/verify.entity.ts create mode 100644 packages/server/src/modules/system.module.ts create mode 100644 packages/server/src/modules/verify.module.ts create mode 100644 packages/server/src/services/system.service.ts create mode 100644 packages/server/src/services/verify.service.ts diff --git a/config/dev.yaml b/config/dev.yaml index b38705e..730569d 100644 --- a/config/dev.yaml +++ b/config/dev.yaml @@ -23,6 +23,15 @@ server: enableRateLimit: true # 是否限流 rateLimitWindowMs: 60000 # 限流时间 rateLimitMax: 1000 # 单位限流时间内单个 ip 最大访问数量 + email: # 邮箱服务,参考 http://help.163.com/09/1223/14/5R7P6CJ600753VB8.html?servCode=6010376 获取 SMTP 配置 + host: '' + port: 465 + user: '' + password: '' + admin: + name: 'sytemadmin' # 注意修改 + password: 'sytemadmin' # 注意修改 + email: 'sytemadmin@think.com' # 注意修改为真实邮箱地址 # 数据库配置 db: diff --git a/packages/client/src/components/admin/system-config/index.tsx b/packages/client/src/components/admin/system-config/index.tsx new file mode 100644 index 0000000..c1b12c9 --- /dev/null +++ b/packages/client/src/components/admin/system-config/index.tsx @@ -0,0 +1,29 @@ +import { TabPane, Tabs } from '@douyinfe/semi-ui'; +import React from 'react'; + +import { Mail } from './mail'; +import { System } from './system'; + +interface IProps { + tab?: string; + onNavigate: (arg: string) => void; +} + +const TitleMap = { + base: '系统管理', + mail: '邮箱服务', +}; + +export const SystemConfig: React.FC = ({ tab, onNavigate }) => { + return ( + + + + + + + + + + ); +}; diff --git a/packages/client/src/components/admin/system-config/mail/index.tsx b/packages/client/src/components/admin/system-config/mail/index.tsx new file mode 100644 index 0000000..fec4553 --- /dev/null +++ b/packages/client/src/components/admin/system-config/mail/index.tsx @@ -0,0 +1,88 @@ +import { Banner, Button, Form, Toast } from '@douyinfe/semi-ui'; +import { DataRender } from 'components/data-render'; +import { useSystemConfig } from 'data/user'; +import { useToggle } from 'hooks/use-toggle'; +import React, { useCallback } from 'react'; + +export const Mail = () => { + const { data, loading, error, sendTestEmail, updateSystemConfig } = useSystemConfig(); + const [changed, toggleChanged] = useToggle(false); + + const onFormChange = useCallback(() => { + toggleChanged(true); + }, [toggleChanged]); + + const onFinish = useCallback( + (values) => { + updateSystemConfig(values).then(() => { + Toast.success('操作成功'); + }); + }, + [updateSystemConfig] + ); + + return ( + ( +
+ + +
+ + + + + + + + + + + + +
+ )} + /> + ); +}; diff --git a/packages/client/src/components/admin/system-config/system/index.tsx b/packages/client/src/components/admin/system-config/system/index.tsx new file mode 100644 index 0000000..d4c539b --- /dev/null +++ b/packages/client/src/components/admin/system-config/system/index.tsx @@ -0,0 +1,49 @@ +import { Banner, Button, Form, Toast } from '@douyinfe/semi-ui'; +import { DataRender } from 'components/data-render'; +import { useSystemConfig } from 'data/user'; +import { useToggle } from 'hooks/use-toggle'; +import React, { useCallback } from 'react'; + +export const System = () => { + const { data, loading, error, updateSystemConfig } = useSystemConfig(); + const [changed, toggleChanged] = useToggle(false); + + const onFormChange = useCallback(() => { + toggleChanged(true); + }, [toggleChanged]); + + const onFinish = useCallback( + (values) => { + updateSystemConfig(values).then(() => { + Toast.success('操作成功'); + }); + }, + [updateSystemConfig] + ); + + return ( + ( +
+ +
+ + + + +
+ )} + /> + ); +}; diff --git a/packages/client/src/components/document/collaboration/index.tsx b/packages/client/src/components/document/collaboration/index.tsx index ef2d130..0fc6b5a 100644 --- a/packages/client/src/components/document/collaboration/index.tsx +++ b/packages/client/src/components/document/collaboration/index.tsx @@ -16,7 +16,6 @@ import { Tooltip, Typography, } from '@douyinfe/semi-ui'; -import { IUser } from '@think/domains'; import { DataRender } from 'components/data-render'; import { DocumentLinkCopyer } from 'components/document/link'; import { useDoumentMembers } from 'data/document'; @@ -52,6 +51,7 @@ const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, export const DocumentCollaboration: React.FC = ({ wikiId, documentId, disabled = false }) => { const { isMobile } = IsOnMobile.useHook(); const ref = useRef(); + const toastedUsersRef = useRef([]); const { user: currentUser } = useUser(); const [visible, toggleVisible] = useToggle(false); const { users, loading, error, addUser, updateUser, deleteUser } = useDoumentMembers(documentId, { @@ -162,7 +162,10 @@ export const DocumentCollaboration: React.FC = ({ wikiId, documentId, di return joinUser.name !== currentUser.name; }) .forEach((joinUser) => { - Toast.info(`${joinUser.name}-${joinUser.clientId}加入文档`); + if (!toastedUsersRef.current.includes(joinUser.clientId)) { + Toast.info(`${joinUser.name}-${joinUser.clientId}加入文档`); + toastedUsersRef.current.push(joinUser.clientId); + } }); setCollaborationUsers(joinUsers); @@ -171,6 +174,7 @@ export const DocumentCollaboration: React.FC = ({ wikiId, documentId, di event.on(JOIN_USER, handler); return () => { + toastedUsersRef.current = []; event.off(JOIN_USER, handler); }; }, [currentUser]); diff --git a/packages/client/src/components/user/index.tsx b/packages/client/src/components/user/index.tsx index d6bb585..2d4111e 100644 --- a/packages/client/src/components/user/index.tsx +++ b/packages/client/src/components/user/index.tsx @@ -2,7 +2,8 @@ import { IconSpin } from '@douyinfe/semi-icons'; import { Avatar, Button, Dropdown, Typography } from '@douyinfe/semi-ui'; import { useUser } from 'data/user'; import { useToggle } from 'hooks/use-toggle'; -import React from 'react'; +import Router from 'next/router'; +import React, { useCallback } from 'react'; import { UserSetting } from './setting'; @@ -12,6 +13,10 @@ export const User: React.FC = () => { const { user, loading, error, toLogin, logout } = useUser(); const [visible, toggleVisible] = useToggle(false); + const toAdmin = useCallback(() => { + Router.push('/admin'); + }, []); + if (loading) return + + + + + + + + +
+ + + 去登录 + + +
+ + +
+ +
+ + ); +}; + +export default Page; diff --git a/packages/client/src/pages/login/index.tsx b/packages/client/src/pages/login/index.tsx index 0125d6a..0559b23 100644 --- a/packages/client/src/pages/login/index.tsx +++ b/packages/client/src/pages/login/index.tsx @@ -51,9 +51,10 @@ const Page = () => { field="name" label="账户" style={{ width: '100%' }} - placeholder="输入账户名称" - rules={[{ required: true, message: '请输入账户' }]} + placeholder="输入账户名称或邮箱" + rules={[{ required: true, message: '请输入账户或邮箱' }]} >
+ { 登录 diff --git a/packages/client/src/pages/register/index.tsx b/packages/client/src/pages/register/index.tsx index 90651e7..cb6329f 100644 --- a/packages/client/src/pages/register/index.tsx +++ b/packages/client/src/pages/register/index.tsx @@ -1,13 +1,14 @@ -import { Button, Form, Layout, Modal, Space, Typography } from '@douyinfe/semi-ui'; +import { Button, Col, Form, Layout, Modal, Row, Space, Toast, Typography } from '@douyinfe/semi-ui'; import { Author } from 'components/author'; import { LogoImage, LogoText } from 'components/logo'; import { Seo } from 'components/seo'; -import { useAsyncLoading } from 'hooks/use-async-loading'; +import { useRegister, useVerifyCode } from 'data/user'; +import { useInterval } from 'hooks/use-interval'; import { useRouterQuery } from 'hooks/use-router-query'; +import { useToggle } from 'hooks/use-toggle'; import Link from 'next/link'; import Router from 'next/router'; -import React from 'react'; -import { register as registerApi } from 'services/user'; +import React, { useCallback, useState } from 'react'; import styles from './index.module.scss'; @@ -16,21 +17,58 @@ const { Title, Text } = Typography; const Page = () => { const query = useRouterQuery(); - const [registerWithLoading, loading] = useAsyncLoading(registerApi); - const onFinish = (values) => { - registerWithLoading(values).then((res) => { - Modal.confirm({ - title: 注册成功, - content: 是否跳转至登录?, - okText: '确认', - cancelText: '取消', - onOk() { - Router.push('/login'); - }, - }); + const [email, setEmail] = useState(''); + const [hasSendVerifyCode, toggleHasSendVerifyCode] = useToggle(false); + const [countDown, setCountDown] = useState(0); + const { register, loading } = useRegister(); + const { sendVerifyCode, loading: sendVerifyCodeLoading } = useVerifyCode(); + + const onFormChange = useCallback((formState) => { + setEmail(formState.values.email); + }, []); + + const { start, stop } = useInterval(() => { + setCountDown((v) => { + if (v - 1 <= 0) { + stop(); + toggleHasSendVerifyCode(false); + return 0; + } + return v - 1; }); - }; + }, 1000); + + const onFinish = useCallback( + (values) => { + register(values).then((res) => { + Modal.confirm({ + title: 注册成功, + content: 是否跳转至登录?, + okText: '确认', + cancelText: '取消', + onOk() { + Router.push('/login', { query }); + }, + }); + }); + }, + [register, query] + ); + + const getVerifyCode = useCallback(() => { + stop(); + sendVerifyCode({ email }) + .then(() => { + Toast.success('请前往邮箱查收验证码'); + setCountDown(60); + start(); + toggleHasSendVerifyCode(true); + }) + .catch(() => { + toggleHasSendVerifyCode(false); + }); + }, [email, toggleHasSendVerifyCode, sendVerifyCode, start, stop]); return ( @@ -42,7 +80,12 @@ const Page = () => { -
+ 用户注册 @@ -72,6 +115,40 @@ const Page = () => { placeholder="确认用户密码" rules={[{ required: true, message: '请再次输入密码' }]} > + + + + + + + + + + + + diff --git a/packages/domains/lib/api/user.d.ts b/packages/domains/lib/api/user.d.ts index 485502c..ea48ec9 100644 --- a/packages/domains/lib/api/user.d.ts +++ b/packages/domains/lib/api/user.d.ts @@ -7,6 +7,14 @@ export declare const UserApiDefinition: { server: "/"; client: () => string; }; + /** + * 获取验证码 + */ + sendVerifyCode: { + method: "get"; + server: "sendVerifyCode"; + client: () => string; + }; /** * 注册 */ @@ -15,6 +23,14 @@ export declare const UserApiDefinition: { server: "register"; client: () => string; }; + /** + * 重置密码 + */ + resetPassword: { + method: "post"; + server: "resetPassword"; + client: () => string; + }; /** * 登录 */ @@ -39,4 +55,36 @@ export declare const UserApiDefinition: { server: "update"; client: () => string; }; + /** + * 锁定用户 + */ + toggleLockUser: { + method: "post"; + server: "lock/user"; + client: () => string; + }; + /** + * 获取系统配置 + */ + getSystemConfig: { + method: "get"; + server: "config/system"; + client: () => string; + }; + /** + * 发送测试邮件 + */ + sendTestEmail: { + method: "get"; + server: "config/system/sendTestEmail"; + client: () => string; + }; + /** + * 发送测试邮件 + */ + updateSystemConfig: { + method: "post"; + server: "config/system/updateSystemConfig"; + client: () => string; + }; }; diff --git a/packages/domains/lib/api/user.js b/packages/domains/lib/api/user.js index 8729c55..1f687f3 100644 --- a/packages/domains/lib/api/user.js +++ b/packages/domains/lib/api/user.js @@ -10,6 +10,14 @@ exports.UserApiDefinition = { server: '/', client: function () { return '/user'; } }, + /** + * 获取验证码 + */ + sendVerifyCode: { + method: 'get', + server: 'sendVerifyCode', + client: function () { return '/verify/sendVerifyCode'; } + }, /** * 注册 */ @@ -18,6 +26,14 @@ exports.UserApiDefinition = { server: 'register', client: function () { return '/user/register'; } }, + /** + * 重置密码 + */ + resetPassword: { + method: 'post', + server: 'resetPassword', + client: function () { return '/user/resetPassword'; } + }, /** * 登录 */ @@ -41,5 +57,37 @@ exports.UserApiDefinition = { method: 'patch', server: 'update', client: function () { return "/user/update"; } + }, + /** + * 锁定用户 + */ + toggleLockUser: { + method: 'post', + server: 'lock/user', + client: function () { return "/user/lock/user"; } + }, + /** + * 获取系统配置 + */ + getSystemConfig: { + method: 'get', + server: 'config/system', + client: function () { return "/user/config/system"; } + }, + /** + * 发送测试邮件 + */ + sendTestEmail: { + method: 'get', + server: 'config/system/sendTestEmail', + client: function () { return "/user/config/system/sendTestEmail"; } + }, + /** + * 发送测试邮件 + */ + updateSystemConfig: { + method: 'post', + server: 'config/system/updateSystemConfig', + client: function () { return "/user/config/system/updateSystemConfig"; } } }; diff --git a/packages/domains/lib/models/index.d.ts b/packages/domains/lib/models/index.d.ts index f9692ca..88796f1 100644 --- a/packages/domains/lib/models/index.d.ts +++ b/packages/domains/lib/models/index.d.ts @@ -5,3 +5,4 @@ export * from './message'; export * from './template'; export * from './comment'; export * from './pagination'; +export * from './system'; diff --git a/packages/domains/lib/models/index.js b/packages/domains/lib/models/index.js index d3a790c..ef200cb 100644 --- a/packages/domains/lib/models/index.js +++ b/packages/domains/lib/models/index.js @@ -17,3 +17,4 @@ __exportStar(require("./message"), exports); __exportStar(require("./template"), exports); __exportStar(require("./comment"), exports); __exportStar(require("./pagination"), exports); +__exportStar(require("./system"), exports); diff --git a/packages/domains/lib/models/system.d.ts b/packages/domains/lib/models/system.d.ts new file mode 100644 index 0000000..c395163 --- /dev/null +++ b/packages/domains/lib/models/system.d.ts @@ -0,0 +1,7 @@ +export interface ISystemConfig { + isSystemLocked: boolean; + emailServiceHost: string; + emailServicePassword: string; + emailServicePort: string; + emailServiceUser: string; +} diff --git a/packages/domains/lib/models/system.js b/packages/domains/lib/models/system.js new file mode 100644 index 0000000..0e34578 --- /dev/null +++ b/packages/domains/lib/models/system.js @@ -0,0 +1,2 @@ +"use strict"; +exports.__esModule = true; diff --git a/packages/domains/lib/models/user.d.ts b/packages/domains/lib/models/user.d.ts index d832b43..7b11ff5 100644 --- a/packages/domains/lib/models/user.d.ts +++ b/packages/domains/lib/models/user.d.ts @@ -24,6 +24,7 @@ export interface IUser { email?: string; role: UserRole; status: UserStatus; + isSystemAdmin?: boolean; } /** * 登录用户数据定义 diff --git a/packages/domains/src/api/user.ts b/packages/domains/src/api/user.ts index 843e4a9..56b1d26 100644 --- a/packages/domains/src/api/user.ts +++ b/packages/domains/src/api/user.ts @@ -10,6 +10,15 @@ export const UserApiDefinition = { client: () => '/user', }, + /** + * 获取验证码 + */ + sendVerifyCode: { + method: 'get' as const, + server: 'sendVerifyCode' as const, + client: () => '/verify/sendVerifyCode', + }, + /** * 注册 */ @@ -19,6 +28,15 @@ export const UserApiDefinition = { client: () => '/user/register', }, + /** + * 重置密码 + */ + resetPassword: { + method: 'post' as const, + server: 'resetPassword' as const, + client: () => '/user/resetPassword', + }, + /** * 登录 */ @@ -45,4 +63,40 @@ export const UserApiDefinition = { server: 'update' as const, client: () => `/user/update`, }, + + /** + * 锁定用户 + */ + toggleLockUser: { + method: 'post' as const, + server: 'lock/user' as const, + client: () => `/user/lock/user`, + }, + + /** + * 获取系统配置 + */ + getSystemConfig: { + method: 'get' as const, + server: 'config/system' as const, + client: () => `/user/config/system`, + }, + + /** + * 发送测试邮件 + */ + sendTestEmail: { + method: 'get' as const, + server: 'config/system/sendTestEmail' as const, + client: () => `/user/config/system/sendTestEmail`, + }, + + /** + * 发送测试邮件 + */ + updateSystemConfig: { + method: 'post' as const, + server: 'config/system/updateSystemConfig' as const, + client: () => `/user/config/system/updateSystemConfig`, + }, }; diff --git a/packages/domains/src/models/index.ts b/packages/domains/src/models/index.ts index f9692ca..88796f1 100644 --- a/packages/domains/src/models/index.ts +++ b/packages/domains/src/models/index.ts @@ -5,3 +5,4 @@ export * from './message'; export * from './template'; export * from './comment'; export * from './pagination'; +export * from './system'; diff --git a/packages/domains/src/models/system.ts b/packages/domains/src/models/system.ts new file mode 100644 index 0000000..7ec1636 --- /dev/null +++ b/packages/domains/src/models/system.ts @@ -0,0 +1,7 @@ +export interface ISystemConfig { + isSystemLocked: boolean; + emailServiceHost: string; + emailServicePassword: string; + emailServicePort: string; + emailServiceUser: string; +} diff --git a/packages/domains/src/models/user.ts b/packages/domains/src/models/user.ts index 30c4995..0d52ec9 100644 --- a/packages/domains/src/models/user.ts +++ b/packages/domains/src/models/user.ts @@ -26,6 +26,7 @@ export interface IUser { email?: string; role: UserRole; status: UserStatus; + isSystemAdmin?: boolean; } /** diff --git a/packages/server/package.json b/packages/server/package.json index df1bde3..a93c30e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -49,6 +49,7 @@ "lodash": "^4.17.21", "mysql2": "^2.3.3", "nestjs-pino": "^2.5.2", + "nodemailer": "^6.7.5", "nuid": "^1.1.6", "passport": "^0.5.2", "passport-jwt": "^4.0.0", @@ -61,6 +62,7 @@ "rxjs": "^7.2.0", "typeorm": "^0.2.41", "ua-parser-js": "^1.0.2", + "validator": "^13.7.0", "y-prosemirror": "^1.0.14", "yjs": "^13.5.24" }, diff --git a/packages/server/src/app.module.ts b/packages/server/src/app.module.ts index bfc10bc..ab41797 100644 --- a/packages/server/src/app.module.ts +++ b/packages/server/src/app.module.ts @@ -3,8 +3,10 @@ import { DocumentEntity } from '@entities/document.entity'; import { DocumentAuthorityEntity } from '@entities/document-authority.entity'; import { MessageEntity } from '@entities/message.entity'; import { StarEntity } from '@entities/star.entity'; +import { SystemEntity } from '@entities/system.entity'; import { TemplateEntity } from '@entities/template.entity'; import { UserEntity } from '@entities/user.entity'; +import { VerifyEntity } from '@entities/verify.entity'; import { ViewEntity } from '@entities/view.entity'; import { WikiEntity } from '@entities/wiki.entity'; import { WikiUserEntity } from '@entities/wiki-user.entity'; @@ -15,8 +17,10 @@ import { DocumentModule } from '@modules/document.module'; import { FileModule } from '@modules/file.module'; import { MessageModule } from '@modules/message.module'; import { StarModule } from '@modules/star.module'; +import { SystemModule } from '@modules/system.module'; import { TemplateModule } from '@modules/template.module'; import { UserModule } from '@modules/user.module'; +import { VerifyModule } from '@modules/verify.module'; import { ViewModule } from '@modules/view.module'; import { WikiModule } from '@modules/wiki.module'; import { forwardRef, Inject, Module } from '@nestjs/common'; @@ -40,6 +44,8 @@ const ENTITIES = [ MessageEntity, TemplateEntity, ViewEntity, + VerifyEntity, + SystemEntity, ]; const MODULES = [ @@ -52,6 +58,8 @@ const MODULES = [ MessageModule, TemplateModule, ViewModule, + VerifyModule, + SystemModule, ]; @Module({ diff --git a/packages/server/src/controllers/system.controller.ts b/packages/server/src/controllers/system.controller.ts new file mode 100644 index 0000000..49344c7 --- /dev/null +++ b/packages/server/src/controllers/system.controller.ts @@ -0,0 +1,7 @@ +import { Controller } from '@nestjs/common'; +import { SystemService } from '@services/system.service'; + +@Controller('system') +export class SystemController { + constructor(private readonly systemService: SystemService) {} +} diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index dde8b15..c697e2b 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -1,4 +1,4 @@ -import { CreateUserDto } from '@dtos/create-user.dto'; +import { RegisterUserDto, ResetPasswordDto } from '@dtos/create-user.dto'; import { LoginUserDto } from '@dtos/login-user.dto'; import { UpdateUserDto } from '@dtos/update-user.dto'; import { JwtGuard } from '@guard/jwt.guard'; @@ -11,6 +11,7 @@ import { HttpStatus, Patch, Post, + Query, Request, Res, UseGuards, @@ -41,7 +42,7 @@ export class UserController { @UseInterceptors(ClassSerializerInterceptor) @Post(UserApiDefinition.register.server) @HttpCode(HttpStatus.CREATED) - async register(@Body() user: CreateUserDto) { + async register(@Body() user: RegisterUserDto) { return await this.userService.createUser(user); } @@ -62,6 +63,16 @@ export class UserController { return { ...data, token }; } + /** + * 重置密码 + */ + @UseInterceptors(ClassSerializerInterceptor) + @Post(UserApiDefinition.resetPassword.server) + @HttpCode(HttpStatus.OK) + async resetPassword(@Body() user: ResetPasswordDto) { + return await this.userService.resetPassword(user); + } + /** * 登出 */ @@ -88,4 +99,48 @@ export class UserController { async updateUser(@Request() req, @Body() dto: UpdateUserDto) { return await this.userService.updateUser(req.user, dto); } + + /** + * 获取系统配置 + */ + @UseInterceptors(ClassSerializerInterceptor) + @Get(UserApiDefinition.getSystemConfig.server) + @HttpCode(HttpStatus.OK) + @UseGuards(JwtGuard) + async getSystemConfig(@Request() req) { + return await this.userService.getSystemConfig(req.user); + } + + /** + * 发送测试邮件 + */ + @UseInterceptors(ClassSerializerInterceptor) + @Get(UserApiDefinition.sendTestEmail.server) + @HttpCode(HttpStatus.OK) + @UseGuards(JwtGuard) + async sendTestEmail(@Request() req) { + return await this.userService.sendTestEmail(req.user); + } + + /** + * 更新系统配置 + */ + @UseInterceptors(ClassSerializerInterceptor) + @Post(UserApiDefinition.updateSystemConfig.server) + @HttpCode(HttpStatus.OK) + @UseGuards(JwtGuard) + async toggleLockSystem(@Request() req, @Body() systemConfig) { + return await this.userService.updateSystemConfig(req.user, systemConfig); + } + + /** + * 锁定用户 + */ + @UseInterceptors(ClassSerializerInterceptor) + @Post(UserApiDefinition.toggleLockUser.server) + @HttpCode(HttpStatus.OK) + @UseGuards(JwtGuard) + async toggleLockUser(@Request() req, @Query('targetUserId') targetUserId) { + return await this.userService.toggleLockUser(req.user, targetUserId); + } } diff --git a/packages/server/src/controllers/verify.controller.ts b/packages/server/src/controllers/verify.controller.ts new file mode 100644 index 0000000..7d76b72 --- /dev/null +++ b/packages/server/src/controllers/verify.controller.ts @@ -0,0 +1,23 @@ +import { + ClassSerializerInterceptor, + Controller, + Get, + HttpCode, + HttpStatus, + Query, + UseInterceptors, +} from '@nestjs/common'; +import { VerifyService } from '@services/verify.service'; +import { UserApiDefinition } from '@think/domains'; + +@Controller('verify') +export class VerifyController { + constructor(private readonly verifyService: VerifyService) {} + + @UseInterceptors(ClassSerializerInterceptor) + @Get(UserApiDefinition.sendVerifyCode.server) + @HttpCode(HttpStatus.CREATED) + async sendVerifyCode(@Query('email') email) { + return await this.verifyService.sendVerifyCode(email); + } +} diff --git a/packages/server/src/dtos/create-user.dto.ts b/packages/server/src/dtos/create-user.dto.ts index 2b471c2..c6294a0 100644 --- a/packages/server/src/dtos/create-user.dto.ts +++ b/packages/server/src/dtos/create-user.dto.ts @@ -1,27 +1,57 @@ import { IsEmail, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; -export class CreateUserDto { +/** + * 用户注册 + */ +export class RegisterUserDto { + @MaxLength(20, { message: '用户账号最多20个字符' }) + @MinLength(5, { message: '用户账号至少5个字符' }) @IsString({ message: '用户名称类型错误(正确类型为:String)' }) @IsNotEmpty({ message: '用户账号不能为空' }) - @MinLength(5, { message: '用户账号至少5个字符' }) - @MaxLength(20, { message: '用户账号最多20个字符' }) - readonly name: string; + name: string; + @MinLength(5, { message: '用户密码至少5个字符' }) @IsString({ message: '用户密码类型错误(正确类型为:String)' }) @IsNotEmpty({ message: '用户密码不能为空' }) - @MinLength(5, { message: '用户密码至少5个字符' }) password: string; - @IsString({ message: ' 用户确认密码类型错误(正确类型为:String)' }) - @MinLength(5, { message: '用户密码至少5个字符' }) - readonly confirmPassword: string; + @MinLength(5, { message: '用户二次确认密码至少5个字符' }) + @IsString({ message: '用户二次确认密码类型错误(正确类型为:String)' }) + @IsNotEmpty({ message: '用户二次确认密码不能为空' }) + confirmPassword: string; - @IsString({ message: '用户头像类型错误(正确类型为:String)' }) - @IsOptional() - readonly avatar?: string; + @IsEmail({ message: '请输入正确的邮箱地址' }) + @IsString({ message: '用户邮箱类型错误(正确类型为:String)' }) + @IsNotEmpty({ message: '用户邮箱不能为空' }) + email: string; - @IsString({ message: ' 用户邮箱类型错误(正确类型为:String)' }) - @IsEmail() - @IsOptional() - readonly email?: string; + @MinLength(5, { message: '邮箱验证码至少5个字符' }) + @IsString({ message: '邮箱验证码错误(正确类型为:String)' }) + @IsNotEmpty({ message: '邮箱验证码不能为空' }) + verifyCode: string; +} + +/** + * 重置密码 + */ +export class ResetPasswordDto { + @MinLength(5, { message: '用户密码至少5个字符' }) + @IsString({ message: '用户密码类型错误(正确类型为:String)' }) + @IsNotEmpty({ message: '用户密码不能为空' }) + password: string; + + @MinLength(5, { message: '用户二次确认密码至少5个字符' }) + @IsString({ message: '用户二次确认密码类型错误(正确类型为:String)' }) + @IsNotEmpty({ message: '用户二次确认密码不能为空' }) + confirmPassword: string; + + @IsEmail({ message: '请输入正确的邮箱地址' }) + @IsString({ message: '用户邮箱类型错误(正确类型为:String)' }) + @IsNotEmpty({ message: '用户邮箱不能为空' }) + email: string; + + @MinLength(5, { message: '邮箱验证码至少5个字符' }) + @IsString({ message: '邮箱验证码错误(正确类型为:String)' }) + @IsNotEmpty({ message: '邮箱验证码不能为空' }) + verifyCode: string; } diff --git a/packages/server/src/dtos/login-user.dto.ts b/packages/server/src/dtos/login-user.dto.ts index 5366ff1..4d037dc 100644 --- a/packages/server/src/dtos/login-user.dto.ts +++ b/packages/server/src/dtos/login-user.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator'; +import { IsEmail, IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator'; export class LoginUserDto { @IsString({ message: '用户名称类型错误(正确类型为:String)' }) diff --git a/packages/server/src/entities/system.entity.ts b/packages/server/src/entities/system.entity.ts new file mode 100644 index 0000000..ba9b89f --- /dev/null +++ b/packages/server/src/entities/system.entity.ts @@ -0,0 +1,56 @@ +import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; + +@Entity('system') +export class SystemEntity { + @PrimaryGeneratedColumn('uuid') + public id: string; + + /** + * 是否锁定系统,锁定后除系统管理员外均不可登录,同时禁止注册 + */ + + @Column({ type: 'boolean', default: false, comment: '是否锁定系统' }) + isSystemLocked: boolean; + + /** + * 邮箱服务地址 + */ + + @Column({ type: 'text', default: null }) + emailServiceHost: string; + + /** + * 邮箱服务端口 + */ + + @Column({ type: 'text', default: null }) + emailServicePort: string; + + /** + * 邮箱服务用户名 + */ + + @Column({ type: 'text', default: null }) + emailServiceUser: string; + + /** + * 邮箱服务授权码 + */ + + @Column({ type: 'text', default: null }) + emailServicePassword: string; + + @CreateDateColumn({ + type: 'timestamp', + name: 'createdAt', + comment: '创建时间', + }) + createdAt: Date; + + @UpdateDateColumn({ + type: 'timestamp', + name: 'updatedAt', + comment: '更新时间', + }) + updatedAt: Date; +} diff --git a/packages/server/src/entities/user.entity.ts b/packages/server/src/entities/user.entity.ts index 7f56f0f..aecb806 100644 --- a/packages/server/src/entities/user.entity.ts +++ b/packages/server/src/entities/user.entity.ts @@ -28,12 +28,15 @@ export class UserEntity { @Column({ type: 'varchar', length: 200, comment: '用户加密密码' }) public password: string; - @Column({ type: 'varchar', comment: '头像地址', default: '' }) + @Column({ type: 'varchar', length: 500, comment: '头像地址', default: '' }) public avatar: string; - @Column({ type: 'varchar', comment: '邮箱地址', default: '' }) + @Column({ type: 'varchar', comment: '邮箱地址' }) public email: string; + @Column({ type: 'boolean', default: false, comment: '是否为系统管理员' }) + isSystemAdmin: boolean; + @Column({ type: 'enum', enum: UserRole, diff --git a/packages/server/src/entities/verify.entity.ts b/packages/server/src/entities/verify.entity.ts new file mode 100644 index 0000000..9fafc61 --- /dev/null +++ b/packages/server/src/entities/verify.entity.ts @@ -0,0 +1,27 @@ +import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; + +@Entity('verify') +export class VerifyEntity { + @PrimaryGeneratedColumn('uuid') + public id: string; + + @Column({ type: 'varchar', comment: '邮箱地址' }) + public email: string; + + @Column({ type: 'varchar', comment: '验证码' }) + public verifyCode: string; + + @CreateDateColumn({ + type: 'timestamp', + name: 'createdAt', + comment: '创建时间', + }) + createdAt: Date; + + @UpdateDateColumn({ + type: 'timestamp', + name: 'updatedAt', + comment: '更新时间', + }) + updatedAt: Date; +} diff --git a/packages/server/src/modules/system.module.ts b/packages/server/src/modules/system.module.ts new file mode 100644 index 0000000..c4d4f9c --- /dev/null +++ b/packages/server/src/modules/system.module.ts @@ -0,0 +1,13 @@ +import { SystemController } from '@controllers/system.controller'; +import { SystemEntity } from '@entities/system.entity'; +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SystemService } from '@services/system.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([SystemEntity])], + providers: [SystemService], + exports: [SystemService], + controllers: [SystemController], +}) +export class SystemModule {} diff --git a/packages/server/src/modules/user.module.ts b/packages/server/src/modules/user.module.ts index 6eaf195..c311a3f 100644 --- a/packages/server/src/modules/user.module.ts +++ b/packages/server/src/modules/user.module.ts @@ -13,6 +13,9 @@ import { getConfig } from '@think/config'; import { Request as RequestType } from 'express'; import { ExtractJwt, Strategy } from 'passport-jwt'; +import { SystemModule } from './system.module'; +import { VerifyModule } from './verify.module'; + const config = getConfig(); const jwtConfig = config.jwt as { secretkey: string; @@ -61,6 +64,8 @@ const jwtModule = JwtModule.register({ forwardRef(() => WikiModule), forwardRef(() => MessageModule), forwardRef(() => StarModule), + forwardRef(() => VerifyModule), + forwardRef(() => SystemModule), passModule, jwtModule, ], diff --git a/packages/server/src/modules/verify.module.ts b/packages/server/src/modules/verify.module.ts new file mode 100644 index 0000000..cc00c13 --- /dev/null +++ b/packages/server/src/modules/verify.module.ts @@ -0,0 +1,14 @@ +import { VerifyController } from '@controllers/verify.controller'; +import { VerifyEntity } from '@entities/verify.entity'; +import { SystemModule } from '@modules/system.module'; +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { VerifyService } from '@services/verify.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([VerifyEntity]), forwardRef(() => SystemModule)], + providers: [VerifyService], + exports: [VerifyService], + controllers: [VerifyController], +}) +export class VerifyModule {} diff --git a/packages/server/src/services/system.service.ts b/packages/server/src/services/system.service.ts new file mode 100644 index 0000000..27b4e06 --- /dev/null +++ b/packages/server/src/services/system.service.ts @@ -0,0 +1,123 @@ +import { SystemEntity } from '@entities/system.entity'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import * as nodemailer from 'nodemailer'; +import { Repository } from 'typeorm'; + +@Injectable() +export class SystemService { + constructor( + @InjectRepository(SystemEntity) + private readonly systemRepo: Repository, + + private readonly confifgService: ConfigService + ) { + this.loadFromConfigFile(); + } + + /** + * 从数据库获取配置 + * @returns + */ + public async getConfigFromDatabase() { + const data = await this.systemRepo.find(); + return (data && data[0]) || null; + } + + /** + * 更新系统配置 + * @param patch + * @returns + */ + public async updateConfigInDatabase(patch: Partial) { + const current = await this.getConfigFromDatabase(); + return await this.systemRepo.save(await this.systemRepo.merge(current, patch)); + } + + /** + * 从配置文件载入配置 + */ + private async loadFromConfigFile() { + const currentConfig = await this.getConfigFromDatabase(); + const emailConfigKeys = ['emailServiceHost', 'emailServicePort', 'emailServiceUser', 'emailServicePassword']; + + if (currentConfig && emailConfigKeys.every((configKey) => Boolean(currentConfig[configKey]))) { + return; + } + + // 同步邮件服务配置 + const emailConfigFromConfigFile = await this.confifgService.get('server.email'); + let emailConfig = {}; + if (emailConfigFromConfigFile && typeof emailConfigFromConfigFile === 'object') { + emailConfig = { + emailServiceHost: emailConfigFromConfigFile.host, + emailServicePort: emailConfigFromConfigFile.port, + emailServiceUser: emailConfigFromConfigFile.user, + emailServicePassword: emailConfigFromConfigFile.password, + }; + } + + const newConfig = currentConfig + ? await this.systemRepo.merge(currentConfig, emailConfig) + : await this.systemRepo.create(emailConfig); + await this.systemRepo.save(newConfig); + + console.log('[think] 已载入文件配置:', newConfig); + } + + /** + * 发送邮件 + * @param content + */ + public async sendEmail(mail: { to: string; subject: string; text?: string; html?: string }) { + const config = await this.getConfigFromDatabase(); + + if (!config) { + throw new HttpException('系统未配置邮箱服务,请联系系统管理员', HttpStatus.SERVICE_UNAVAILABLE); + } + + const emailConfig = { + host: config.emailServiceHost, + port: +config.emailServicePort, + user: config.emailServiceUser, + pass: config.emailServicePassword, + }; + + if (Object.keys(emailConfig).some((key) => !emailConfig[key])) { + throw new HttpException('系统邮箱服务配置不完善,请联系系统管理员', HttpStatus.SERVICE_UNAVAILABLE); + } + + const transporter = nodemailer.createTransport({ + host: emailConfig.host, + port: emailConfig.port, + secure: emailConfig.port === 465, + auth: { + user: emailConfig.user, + pass: emailConfig.pass, + }, + }); + + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error(`发送邮件失败`)); + }, 10 * 1000); + + transporter.sendMail( + { + from: emailConfig.user, + ...mail, + }, + (err, info) => { + console.log('fas', err, info); + + if (err) { + reject(err); + } else { + resolve(info); + } + } + ); + }); + } +} diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts index b96fbed..d5c459f 100644 --- a/packages/server/src/services/user.service.ts +++ b/packages/server/src/services/user.service.ts @@ -1,6 +1,7 @@ -import { CreateUserDto } from '@dtos/create-user.dto'; +import { RegisterUserDto, ResetPasswordDto } from '@dtos/create-user.dto'; import { LoginUserDto } from '@dtos/login-user.dto'; import { UpdateUserDto } from '@dtos/update-user.dto'; +import { SystemEntity } from '@entities/system.entity'; import { UserEntity } from '@entities/user.entity'; import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @@ -8,11 +9,14 @@ import { JwtService } from '@nestjs/jwt'; import { InjectRepository } from '@nestjs/typeorm'; import { MessageService } from '@services/message.service'; import { StarService } from '@services/star.service'; +import { VerifyService } from '@services/verify.service'; import { WikiService } from '@services/wiki.service'; import { UserStatus } from '@think/domains'; import { instanceToPlain } from 'class-transformer'; import { Repository } from 'typeorm'; +import { SystemService } from './system.service'; + export type OutUser = Omit; @Injectable() @@ -33,8 +37,47 @@ export class UserService { private readonly starService: StarService, @Inject(forwardRef(() => WikiService)) - private readonly wikiService: WikiService - ) {} + private readonly wikiService: WikiService, + + @Inject(forwardRef(() => VerifyService)) + private readonly verifyService: VerifyService, + + @Inject(forwardRef(() => SystemService)) + private readonly systemService: SystemService + ) { + this.createDefaultSystemAdminFromConfigFile(); + } + + /** + * 从配置文件创建默认系统管理员 + */ + private async createDefaultSystemAdminFromConfigFile() { + if (await this.userRepo.findOne({ isSystemAdmin: true })) { + return; + } + + const config = await this.confifgService.get('server.admin'); + + if (!config.name || !config.password || !config.email) { + throw new Error(`请指定名称、密码和邮箱`); + } + + if (await this.userRepo.findOne({ name: config.name })) { + return; + } + + try { + await this.userRepo.save( + await this.userRepo.create({ + ...config, + isSystemAdmin: true, + }) + ); + console.log('[think] 已创建默认系统管理员,请尽快登录系统修改密码'); + } catch (e) { + console.error(`[think] 创建默认系统管理员失败:`, e.message); + } + } /** * 根据 id 查询用户 @@ -71,7 +114,7 @@ export class UserService { * @param user CreateUserDto * @returns */ - async createUser(user: CreateUserDto): Promise { + async createUser(user: RegisterUserDto): Promise { if (await this.userRepo.findOne({ name: user.name })) { throw new HttpException('该账户已被注册', HttpStatus.BAD_REQUEST); } @@ -88,6 +131,10 @@ export class UserService { throw new HttpException('该邮箱已被注册', HttpStatus.BAD_REQUEST); } + if (!(await this.verifyService.checkVerifyCode(user.email, user.verifyCode))) { + throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST); + } + const res = await this.userRepo.create(user); const createdUser = await this.userRepo.save(res); const wiki = await this.wikiService.createWiki(createdUser, { @@ -105,14 +152,52 @@ export class UserService { return instanceToPlain(createdUser) as OutUser; } + /** + * 重置密码 + * @param registerUser + */ + public async resetPassword(resetPasswordDto: ResetPasswordDto) { + const { email, password, confirmPassword, verifyCode } = resetPasswordDto; + + const inDatabaseUser = await this.userRepo.findOne({ email }); + + if (!inDatabaseUser) { + throw new HttpException('该邮箱尚未注册', HttpStatus.BAD_REQUEST); + } + + if (password !== confirmPassword) { + throw new HttpException('两次密码不一致,请重试', HttpStatus.BAD_REQUEST); + } + + if (!(await this.verifyService.checkVerifyCode(email, verifyCode))) { + throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST); + } + + const user = await this.userRepo.save( + await this.userRepo.merge(inDatabaseUser, { password: UserEntity.encryptPassword(password) }) + ); + + return instanceToPlain(user); + } + /** * 用户登录 * @param user * @returns */ async login(user: LoginUserDto): Promise<{ user: OutUser; token: string; domain: string; expiresIn: number }> { + const currentSystemConfig = await this.systemService.getConfigFromDatabase(); + const { name, password } = user; - const existUser = await this.userRepo.findOne({ where: { name } }); + let existUser = await this.userRepo.findOne({ where: { name } }); + + if (!existUser) { + existUser = await this.userRepo.findOne({ where: { email: name } }); + } + + if (!existUser.isSystemAdmin && currentSystemConfig.isSystemLocked) { + throw new HttpException('系统维护中,暂不可登录', HttpStatus.FORBIDDEN); + } if (!existUser || !(await UserEntity.comparePassword(password, existUser.password))) { throw new HttpException('用户名或密码错误', HttpStatus.BAD_REQUEST); @@ -168,4 +253,80 @@ export class UserService { const [data] = await query.getManyAndCount(); return data; } + + /** + * 锁定或解锁用户 + * @param user + * @param targetUserId + */ + async toggleLockUser(user: UserEntity, targetUserId) { + const currentUser = await this.userRepo.findOne(user.id); + + if (!currentUser.isSystemAdmin) { + throw new HttpException('您无权操作', HttpStatus.FORBIDDEN); + } + + const targetUser = await this.userRepo.findOne(targetUserId); + + if (!targetUser) { + throw new HttpException('目标用户不存在', HttpStatus.NOT_FOUND); + } + + const nextStatus = targetUser.status === UserStatus.normal ? UserStatus.locked : UserStatus.normal; + return await this.userRepo.save(await this.userRepo.merge(targetUser, { status: nextStatus })); + } + + /** + * 获取系统配置 + * @param user + * @returns + */ + async getSystemConfig(user: UserEntity) { + const currentUser = await this.userRepo.findOne(user.id); + + if (!currentUser.isSystemAdmin) { + throw new HttpException('您无权操作', HttpStatus.FORBIDDEN); + } + + return await this.systemService.getConfigFromDatabase(); + } + + /** + * 发送测试邮件 + * @param user + */ + async sendTestEmail(user: UserEntity) { + const currentUser = await this.userRepo.findOne(user.id); + + if (!currentUser.isSystemAdmin) { + throw new HttpException('您无权操作', HttpStatus.FORBIDDEN); + } + + const currentConfig = await this.systemService.getConfigFromDatabase(); + try { + await this.systemService.sendEmail({ + to: currentConfig.emailServiceUser, + subject: '测试邮件', + html: `

测试邮件

`, + }); + return '测试邮件发送成功'; + } catch (err) { + throw new HttpException('测试邮件发送失败!', HttpStatus.BAD_REQUEST); + } + } + + /** + * 更新系统配置 + * @param user + * @param targetUserId + */ + async updateSystemConfig(user: UserEntity, systemConfig: Partial) { + const currentUser = await this.userRepo.findOne(user.id); + + if (!currentUser.isSystemAdmin) { + throw new HttpException('您无权操作', HttpStatus.FORBIDDEN); + } + + return await this.systemService.updateConfigInDatabase(systemConfig); + } } diff --git a/packages/server/src/services/verify.service.ts b/packages/server/src/services/verify.service.ts new file mode 100644 index 0000000..cb3e5f9 --- /dev/null +++ b/packages/server/src/services/verify.service.ts @@ -0,0 +1,69 @@ +import { VerifyEntity } from '@entities/verify.entity'; +import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { SystemService } from '@services/system.service'; +import { randomInt } from 'node:crypto'; +import { Repository } from 'typeorm'; +import { isEmail } from 'validator'; + +@Injectable() +export class VerifyService { + constructor( + @InjectRepository(VerifyEntity) + private readonly verifyRepo: Repository, + + @Inject(forwardRef(() => SystemService)) + private readonly systemService: SystemService + ) {} + + /** + * 删除验证记录 + * @param record + */ + private async deleteVerifyCode(id) { + await this.verifyRepo.remove(await this.verifyRepo.find(id)); + } + + /** + * 向指定邮箱发送验证码 + * @param email + */ + public async sendVerifyCode(email: string) { + if (!email || !isEmail(email)) { + throw new HttpException('请检查邮箱地址', HttpStatus.BAD_REQUEST); + } + + const verifyCode = randomInt(1000000).toString().padStart(6, '0'); + const record = await this.verifyRepo.save(await this.verifyRepo.create({ email, verifyCode })); + await this.systemService.sendEmail({ + to: email, + subject: '验证码', + html: `

您的验证码为 ${verifyCode}

`, + }); + + const timer = setTimeout(() => { + this.deleteVerifyCode(record.id); + clearTimeout(timer); + }, 5 * 60 * 1000); + } + + /** + * 检验验证码 + * @param email + * @param verifyCode + * @returns + */ + public async checkVerifyCode(email: string, verifyCode: string) { + if (!email || !isEmail(email)) { + throw new HttpException('请检查邮箱地址', HttpStatus.BAD_REQUEST); + } + + const ret = await this.verifyRepo.findOne({ email, verifyCode }); + + if (!ret) { + throw new HttpException('验证码错误', HttpStatus.BAD_REQUEST); + } + + return Boolean(ret); + } +} diff --git a/packages/server/src/services/wiki.service.ts b/packages/server/src/services/wiki.service.ts index 5070c5f..e5e943d 100644 --- a/packages/server/src/services/wiki.service.ts +++ b/packages/server/src/services/wiki.service.ts @@ -326,6 +326,10 @@ export class WikiService { const withHomeDocumentIdWiki = await this.wikiRepo.merge(wiki, { homeDocumentId }); await this.wikiRepo.save(withHomeDocumentIdWiki); + await this.starService.toggleStar(user, { + wikiId: wiki.id, + }); + return withHomeDocumentIdWiki; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae2f521..ea992e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: 5.3 +lockfileVersion: 5.4 importers: @@ -36,7 +36,7 @@ importers: stylelint-config-standard: 25.0.0_stylelint@14.6.1 stylelint-config-standard-scss: 3.0.0_stylelint@14.6.1 stylelint-order: 5.0.0_stylelint@14.6.1 - stylelint-prettier: 2.0.0_prettier@2.5.1+stylelint@14.6.1 + stylelint-prettier: 2.0.0_x3dt43yf7vqctazx2b26xrlr7q typescript: 4.5.5 packages/client: @@ -153,49 +153,49 @@ importers: dependencies: '@douyinfe/semi-icons': 2.3.1_react@17.0.2 '@douyinfe/semi-next': 2.3.1 - '@douyinfe/semi-ui': 2.3.1_b3482aaf5744fc7c2aeb7941b0e0a78f + '@douyinfe/semi-ui': 2.3.1_wnecvl2xit6hykxlpfa3byfhr4 '@hocuspocus/provider': 1.0.0-alpha.29 '@think/config': link:../config '@think/constants': link:../constants '@think/domains': link:../domains '@tiptap/core': 2.0.0-beta.171 - '@tiptap/extension-blockquote': 2.0.0-beta.26_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-bold': 2.0.0-beta.25_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-bullet-list': 2.0.0-beta.26_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-code': 2.0.0-beta.26_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-code-block': 2.0.0-beta.37_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-code-block-lowlight': 2.0.0-beta.68_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-color': 2.0.0-beta.9_e32f4205d0966701340db1456434a591 - '@tiptap/extension-document': 2.0.0-beta.15_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-dropcursor': 2.0.0-beta.25_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-gapcursor': 2.0.0-beta.34_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-hard-break': 2.0.0-beta.30_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-heading': 2.0.0-beta.26_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-highlight': 2.0.0-beta.33_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-history': 2.0.0-beta.21_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-image': 2.0.0-beta.25_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-italic': 2.0.0-beta.25_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-link': 2.0.0-beta.36_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-list-item': 2.0.0-beta.20_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-mention': 2.0.0-beta.95_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-ordered-list': 2.0.0-beta.27_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-paragraph': 2.0.0-beta.23_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-placeholder': 2.0.0-beta.47_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-strike': 2.0.0-beta.27_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-subscript': 2.0.0-beta.10_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-superscript': 2.0.0-beta.10_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-table': 2.0.0-beta.48_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-table-cell': 2.0.0-beta.20_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-table-header': 2.0.0-beta.22_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-table-row': 2.0.0-beta.19_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-task-item': 2.0.0-beta.31_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-task-list': 2.0.0-beta.26_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-text': 2.0.0-beta.15_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-text-align': 2.0.0-beta.29_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-text-style': 2.0.0-beta.23_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-underline': 2.0.0-beta.23_@tiptap+core@2.0.0-beta.171 - '@tiptap/react': 2.0.0-beta.107_a3fcdb91535fe17b69dfabaa94f3bb3d - '@tiptap/suggestion': 2.0.0-beta.90_@tiptap+core@2.0.0-beta.171 + '@tiptap/extension-blockquote': 2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-bold': 2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-bullet-list': 2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-code': 2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-code-block': 2.0.0-beta.37_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-code-block-lowlight': 2.0.0-beta.68_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-color': 2.0.0-beta.9_4mxueboqsztqcnanwfcwinffse + '@tiptap/extension-document': 2.0.0-beta.15_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-dropcursor': 2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-gapcursor': 2.0.0-beta.34_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-hard-break': 2.0.0-beta.30_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-heading': 2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-highlight': 2.0.0-beta.33_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-history': 2.0.0-beta.21_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-image': 2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-italic': 2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-link': 2.0.0-beta.36_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-list-item': 2.0.0-beta.20_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-mention': 2.0.0-beta.95_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-ordered-list': 2.0.0-beta.27_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-paragraph': 2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-placeholder': 2.0.0-beta.47_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-strike': 2.0.0-beta.27_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-subscript': 2.0.0-beta.10_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-superscript': 2.0.0-beta.10_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-table': 2.0.0-beta.48_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-table-cell': 2.0.0-beta.20_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-table-header': 2.0.0-beta.22_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-table-row': 2.0.0-beta.19_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-task-item': 2.0.0-beta.31_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-task-list': 2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-text': 2.0.0-beta.15_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-text-align': 2.0.0-beta.29_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-text-style': 2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-underline': 2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi + '@tiptap/react': 2.0.0-beta.107_up6nxektl7qxw2o7vovjj453hu + '@tiptap/suggestion': 2.0.0-beta.90_neatuoot4hdn6bibaypc754kfi axios: 0.25.0 buffer-image-size: 0.6.4 classnames: 2.3.1 @@ -216,26 +216,26 @@ importers: markdown-it-emoji: 2.0.0 markdown-it-sub: 1.0.0 markdown-it-sup: 1.0.0 - next: 12.1.0_react-dom@17.0.2+react@17.0.2 + next: 12.1.0_sfoxds7t5ydpegc3knd667wn6m next-pwa: 5.5.2_next@12.1.0 - prosemirror-codemark: 0.3.6_4cf2d6daa1edf7cc132d3e7b74beaf4d + prosemirror-codemark: 0.3.6_jtznnwvb5x34yeznhz5xjpvpju prosemirror-commands: 1.3.0 prosemirror-markdown: 1.7.0 prosemirror-model: 1.16.1 prosemirror-schema-list: 1.1.6 prosemirror-state: 1.3.4 prosemirror-tables: 1.1.1 - prosemirror-utils: 0.9.6_4f14324fc97f6af3c95cc9ca12f2bcb1 + prosemirror-utils: 0.9.6_j4kdet6jp5vphsk4zhfbf4v4we prosemirror-view: 1.23.6 react: 17.0.2 - react-countdown: 2.3.2_react-dom@17.0.2+react@17.0.2 + react-countdown: 2.3.2_sfoxds7t5ydpegc3knd667wn6m react-dom: 17.0.2_react@17.0.2 react-helmet: 6.1.0_react@17.0.2 - react-lazy-load-image-component: 1.5.4_react-dom@17.0.2+react@17.0.2 - react-pdf: 5.7.2_react-dom@17.0.2+react@17.0.2 - react-query: 3.39.0_react-dom@17.0.2+react@17.0.2 - react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2 - react-visibility-sensor: 5.1.1_react-dom@17.0.2+react@17.0.2 + react-lazy-load-image-component: 1.5.4_sfoxds7t5ydpegc3knd667wn6m + react-pdf: 5.7.2_sfoxds7t5ydpegc3knd667wn6m + react-query: 3.39.0_sfoxds7t5ydpegc3knd667wn6m + react-split-pane: 0.1.92_sfoxds7t5ydpegc3knd667wn6m + react-visibility-sensor: 5.1.1_sfoxds7t5ydpegc3knd667wn6m requestidlecallback-polyfill: 1.0.2 resize-observer-polyfill: 1.5.1 scroll-into-view-if-needed: 2.2.29 @@ -248,13 +248,13 @@ importers: devDependencies: '@types/node': 17.0.13 '@types/react': 17.0.38 - '@typescript-eslint/eslint-plugin': 5.21.0_19515efd875c7ffcd67055d8be736b9f - '@typescript-eslint/parser': 5.21.0_eslint@8.14.0+typescript@4.5.5 + '@typescript-eslint/eslint-plugin': 5.21.0_dfiv57mhlr77zvtqkxml443lt4 + '@typescript-eslint/parser': 5.21.0_sdq423wcawycjzugemfabbjutq copy-webpack-plugin: 11.0.0 eslint: 8.14.0 eslint-config-prettier: 8.5.0_eslint@8.14.0 - eslint-plugin-import: 2.26.0_eslint@8.14.0 - eslint-plugin-prettier: 4.0.0_74ebb802163a9b4fa8f89d76ed02f62a + eslint-plugin-import: 2.26.0_5yvnzvdwzksrnum37j3gumwy4y + eslint-plugin-prettier: 4.0.0_otv3qaqwhknu7khytv3o2axwfi eslint-plugin-react: 7.29.4_eslint@8.14.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.14.0 eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0 @@ -331,6 +331,7 @@ importers: lodash: ^4.17.21 mysql2: ^2.3.3 nestjs-pino: ^2.5.2 + nodemailer: ^6.7.5 nuid: ^1.1.6 passport: ^0.5.2 passport-jwt: ^4.0.0 @@ -351,19 +352,20 @@ importers: typeorm: ^0.2.41 typescript: ^4.3.5 ua-parser-js: ^1.0.2 + validator: ^13.7.0 y-prosemirror: ^1.0.14 yjs: ^13.5.24 dependencies: '@hocuspocus/server': 1.0.0-alpha.91 '@hocuspocus/transformer': 1.0.0-alpha.18 - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca - '@nestjs/config': 1.1.6_0f75cad7c3bc55c714d492fe95499bdf - '@nestjs/core': 8.2.6_2d4ee36c4446df4873bb38fb1c4583da + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi + '@nestjs/config': 1.1.6_b524vv6dxrk4ofgusl7jksm334 + '@nestjs/core': 8.2.6_fvhog3cei3puq453hd5ryrmd3i '@nestjs/jwt': 8.0.0_@nestjs+common@8.2.6 - '@nestjs/passport': 8.1.0_469354efd308cbdee43194b5fd6a2c15 - '@nestjs/platform-express': 8.2.6_732a54a2558f64827b8cc4f9baac4f1f - '@nestjs/schedule': 2.0.1_d86dd164971a98d71cc003a4f36ff106 - '@nestjs/typeorm': 8.0.3_7d295ee70f5e60cc7bb984ed65836a4c + '@nestjs/passport': 8.1.0_i2jvj36tbdf55zbrss2722rmcu + '@nestjs/platform-express': 8.2.6_omvfjisvr5sie64myt43vlcpd4 + '@nestjs/schedule': 2.0.1_3bw5czexdkmnohgaaospg37ray + '@nestjs/typeorm': 8.0.3_puuv5zyplzqmy65zqtwwla3kjq '@think/config': link:../config '@think/constants': link:../constants '@think/domains': link:../domains @@ -384,7 +386,8 @@ importers: ioredis: 5.0.1 lodash: 4.17.21 mysql2: 2.3.3 - nestjs-pino: 2.5.2_61f1f701bb68c430ae2e1a4fec1d7a2d + nestjs-pino: 2.5.2_mhy7oan3ndcdblrodjh6yhl2fu + nodemailer: 6.7.5 nuid: 1.1.6 passport: 0.5.2 passport-jwt: 4.0.0 @@ -397,12 +400,13 @@ importers: rxjs: 7.5.2 typeorm: 0.2.41_ioredis@5.0.1+mysql2@2.3.3 ua-parser-js: 1.0.2 - y-prosemirror: 1.0.14_8fd72c89aecefb95d86a797b5207d945 + validator: 13.7.0 + y-prosemirror: 1.0.14_r7lszcnoz35zlwdkpf5veb6ziu yjs: 13.5.24 devDependencies: '@nestjs/cli': 8.2.0_eslint@8.14.0 '@nestjs/schematics': 8.0.5_typescript@4.5.5 - '@nestjs/testing': 8.2.6_b893ca8083ee374883b6d648098a9aeb + '@nestjs/testing': 8.2.6_xcj4vaed5y3ura5w2zeatcu25m '@types/cookie-parser': 1.4.3 '@types/cron': 2.0.0 '@types/express': 4.17.13 @@ -410,20 +414,20 @@ importers: '@types/lodash': 4.14.182 '@types/node': 16.11.21 '@types/supertest': 2.0.11 - '@typescript-eslint/eslint-plugin': 5.21.0_19515efd875c7ffcd67055d8be736b9f - '@typescript-eslint/parser': 5.21.0_eslint@8.14.0+typescript@4.5.5 + '@typescript-eslint/eslint-plugin': 5.21.0_dfiv57mhlr77zvtqkxml443lt4 + '@typescript-eslint/parser': 5.21.0_sdq423wcawycjzugemfabbjutq eslint: 8.14.0 eslint-config-prettier: 8.5.0_eslint@8.14.0 - eslint-plugin-import: 2.26.0_eslint@8.14.0 - eslint-plugin-prettier: 4.0.0_740be41c8168d0cc214a306089357ad0 + eslint-plugin-import: 2.26.0_5yvnzvdwzksrnum37j3gumwy4y + eslint-plugin-prettier: 4.0.0_oqf6ihebndimyikkgbqisnl22a eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0 jest: 27.4.7_ts-node@10.4.0 prettier: 2.5.1 source-map-support: 0.5.21 supertest: 6.2.2 - ts-jest: 27.1.3_bc464af956686ccd28ad646067711a2c + ts-jest: 27.1.3_xrdev6kwnbwm2kfnmrqgo4i2fq ts-loader: 9.2.6_typescript@4.5.5 - ts-node: 10.4.0_06de4b00c69b73d094e2c5b522a6ad57 + ts-node: 10.4.0_a3pewaggtnz5bfhcyw2sfjvnk4 tsconfig-paths: 3.12.0 typescript: 4.5.5 @@ -882,11 +886,15 @@ packages: resolution: {integrity: sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==} engines: {node: '>=6.0.0'} hasBin: true + dependencies: + '@babel/types': 7.17.12 /@babel/parser/7.17.12: resolution: {integrity: sha512-FLzHmN9V3AJIrWfOpvRlZCeVg/WLdicSnTMsLur6uDj9TT8ymUlG9XxURdW/XvuygK+2CW0poOJABdA4m/YKxA==} engines: {node: '>=6.0.0'} hasBin: true + dependencies: + '@babel/types': 7.17.12 dev: false /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.17.12_@babel+core@7.16.12: @@ -1788,7 +1796,7 @@ packages: '@cspotcode/source-map-consumer': 0.8.0 dev: true - /@douyinfe/semi-animation-react/2.3.1_react-dom@17.0.2+react@17.0.2: + /@douyinfe/semi-animation-react/2.3.1_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-7rXD60qnRbWQK9et1LfHmoDaL8icavO+gozAMVNYvIsJqz4UPsJdvqAz2tJ0aZRoYzWi39V5mxFnXdzzbcAzMQ==} peerDependencies: prop-types: 15.7.2 @@ -1866,7 +1874,7 @@ packages: glob: 7.2.0 dev: false - /@douyinfe/semi-ui/2.3.1_b3482aaf5744fc7c2aeb7941b0e0a78f: + /@douyinfe/semi-ui/2.3.1_wnecvl2xit6hykxlpfa3byfhr4: resolution: {integrity: sha512-2RlhndHE4J82QSMnioerWMDzlRe+Qe3kJKQo0/6U+LkY2gP9B2edMiTG5QiqQNJ8YeAwOCkKbuJPbgr/B2VdXA==} peerDependencies: '@types/react': '>=16.0.0' @@ -1876,7 +1884,7 @@ packages: react-dom: '>=16.0.0' dependencies: '@babel/runtime-corejs3': 7.16.8 - '@douyinfe/semi-animation-react': 2.3.1_react-dom@17.0.2+react@17.0.2 + '@douyinfe/semi-animation-react': 2.3.1_sfoxds7t5ydpegc3knd667wn6m '@douyinfe/semi-foundation': 2.3.1 '@douyinfe/semi-icons': 2.3.1_react@17.0.2 '@douyinfe/semi-illustrations': 2.3.1_react@17.0.2 @@ -1891,9 +1899,9 @@ packages: lodash: 4.17.21 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 - react-resizable: 1.11.1_react-dom@17.0.2+react@17.0.2 - react-sortable-hoc: 1.11.0_react-dom@17.0.2+react@17.0.2 - react-window: 1.8.6_react-dom@17.0.2+react@17.0.2 + react-resizable: 1.11.1_sfoxds7t5ydpegc3knd667wn6m + react-sortable-hoc: 1.11.0_sfoxds7t5ydpegc3knd667wn6m + react-window: 1.8.6_sfoxds7t5ydpegc3knd667wn6m resize-observer-polyfill: 1.5.1 scroll-into-view-if-needed: 2.2.29 utility-types: 3.10.0 @@ -1975,7 +1983,7 @@ packages: dependencies: '@tiptap/core': 2.0.0-beta.171 prosemirror-model: 1.16.1 - y-prosemirror: 1.0.14_8fd72c89aecefb95d86a797b5207d945 + y-prosemirror: 1.0.14_r7lszcnoz35zlwdkpf5veb6ziu yjs: 13.5.24 transitivePeerDependencies: - prosemirror-state @@ -2255,12 +2263,12 @@ packages: '@angular-devkit/core': 13.1.2_chokidar@3.5.2 '@angular-devkit/schematics': 13.1.2_chokidar@3.5.2 '@angular-devkit/schematics-cli': 13.1.2_chokidar@3.5.2 - '@nestjs/schematics': 8.0.5_chokidar@3.5.2+typescript@4.5.4 + '@nestjs/schematics': 8.0.5_zkb6ykccqtyv52xru36vc75nne chalk: 3.0.0 chokidar: 3.5.2 cli-table3: 0.6.1 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 6.5.0_787dd39517260957bc59f00cd4915d0b + fork-ts-checker-webpack-plugin: 6.5.0_pb65hfixeyevppcz6agnjek5bm inquirer: 7.3.3 node-emoji: 1.11.0 ora: 5.4.1 @@ -2283,7 +2291,7 @@ packages: - webpack-cli dev: true - /@nestjs/common/8.2.6_e2dd7264172553abc41c1a07952dd8ca: + /@nestjs/common/8.2.6_4loxezaxevj2xra4didzkloyzi: resolution: {integrity: sha512-flLYSXunxcKyjbYddrhwbc49uE705MxBt85rS3mHyhDbAIPSGGeZEqME44YyAzCg1NTfJSNe7ztmOce5kNkb9A==} peerDependencies: cache-manager: '*' @@ -2309,16 +2317,15 @@ packages: uuid: 8.3.2 transitivePeerDependencies: - debug - dev: false - /@nestjs/config/1.1.6_0f75cad7c3bc55c714d492fe95499bdf: + /@nestjs/config/1.1.6_b524vv6dxrk4ofgusl7jksm334: resolution: {integrity: sha512-HYizKt6Dr6gcZl8FmZbTfQxP0MG8oXMh+gVFT0XCwYDAq26BOKyhPsIxrKsryicVeKViRgetCUhlJY9EqaekZA==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 reflect-metadata: ^0.1.13 rxjs: ^6.0.0 || ^7.2.0 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi dotenv: 10.0.0 dotenv-expand: 5.1.0 lodash: 4.17.21 @@ -2327,7 +2334,7 @@ packages: uuid: 8.3.2 dev: false - /@nestjs/core/8.2.6_2d4ee36c4446df4873bb38fb1c4583da: + /@nestjs/core/8.2.6_fvhog3cei3puq453hd5ryrmd3i: resolution: {integrity: sha512-NwPcEIMmCsucs3QaDlQvkoU1FlFM2wm/WjaqLQhkSoIEmAR1gNtBo88f5io5cpMwCo1k5xYhqGlaSl6TfngwWQ==} requiresBuild: true peerDependencies: @@ -2345,8 +2352,8 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca - '@nestjs/platform-express': 8.2.6_732a54a2558f64827b8cc4f9baac4f1f + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi + '@nestjs/platform-express': 8.2.6_omvfjisvr5sie64myt43vlcpd4 '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -2358,72 +2365,57 @@ packages: uuid: 8.3.2 transitivePeerDependencies: - encoding - dev: false /@nestjs/jwt/8.0.0_@nestjs+common@8.2.6: resolution: {integrity: sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==} peerDependencies: '@nestjs/common': ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi '@types/jsonwebtoken': 8.5.4 jsonwebtoken: 8.5.1 dev: false - /@nestjs/passport/8.1.0_469354efd308cbdee43194b5fd6a2c15: + /@nestjs/passport/8.1.0_i2jvj36tbdf55zbrss2722rmcu: resolution: {integrity: sha512-IJ0AumoIWPzzki8d1TixTLcWJiNhJxzpn9ejAYA0TaKsC2B1zyg58WotTU3U/perCOiiAgjpvTB4vA/7L8K9Mg==} peerDependencies: '@nestjs/common': ^6.0.0 || ^7.0.0 || ^8.0.0 passport: ^0.4.0 || ^0.5.0 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi passport: 0.5.2 dev: false - /@nestjs/platform-express/8.2.6_732a54a2558f64827b8cc4f9baac4f1f: + /@nestjs/platform-express/8.2.6_omvfjisvr5sie64myt43vlcpd4: resolution: {integrity: sha512-wbPqXrLdeokfMCHkWBHgFobCVL4OKRAOJIFGNlT/3u4JIJndoGBIuSDQohhY2o7Ue0JIYqKw+PyXiN4y/iUEng==} peerDependencies: '@nestjs/common': ^8.0.0 '@nestjs/core': ^8.0.0 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca - '@nestjs/core': 8.2.6_2d4ee36c4446df4873bb38fb1c4583da + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi + '@nestjs/core': 8.2.6_fvhog3cei3puq453hd5ryrmd3i body-parser: 1.19.1 cors: 2.8.5 express: 4.17.2 multer: 1.4.4 tslib: 2.3.1 - dev: false + transitivePeerDependencies: + - supports-color - /@nestjs/schedule/2.0.1_d86dd164971a98d71cc003a4f36ff106: + /@nestjs/schedule/2.0.1_3bw5czexdkmnohgaaospg37ray: resolution: {integrity: sha512-NqiCk3P7HDMw55kpefNIzAAQEsP+6dDIXUt4/KQANtAZ+opdLzo8rkzI0j8vDqgYeTh+PKq+V6zwSRjR61xPAQ==} peerDependencies: '@nestjs/common': ^6.10.11 || ^7.0.0 || ^8.0.0 '@nestjs/core': ^7.0.0 || ^8.0.0 reflect-metadata: ^0.1.12 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca - '@nestjs/core': 8.2.6_2d4ee36c4446df4873bb38fb1c4583da + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi + '@nestjs/core': 8.2.6_fvhog3cei3puq453hd5ryrmd3i cron: 2.0.0 reflect-metadata: 0.1.13 uuid: 8.3.2 dev: false - /@nestjs/schematics/8.0.5_chokidar@3.5.2+typescript@4.5.4: - resolution: {integrity: sha512-nK1hWQeLNbdhsiJDX/XJXLqq7nC6/xxC8CN+seFTQmly+H3gG2xaFnl6JPHURumuQaYJX8JEpC8m0+4tz+wvOg==} - peerDependencies: - typescript: ^3.4.5 || ^4.3.5 - dependencies: - '@angular-devkit/core': 13.0.2_chokidar@3.5.2 - '@angular-devkit/schematics': 13.0.2_chokidar@3.5.2 - fs-extra: 10.0.0 - jsonc-parser: 3.0.0 - pluralize: 8.0.0 - typescript: 4.5.4 - transitivePeerDependencies: - - chokidar - dev: true - /@nestjs/schematics/8.0.5_typescript@4.5.5: resolution: {integrity: sha512-nK1hWQeLNbdhsiJDX/XJXLqq7nC6/xxC8CN+seFTQmly+H3gG2xaFnl6JPHURumuQaYJX8JEpC8m0+4tz+wvOg==} peerDependencies: @@ -2439,7 +2431,22 @@ packages: - chokidar dev: true - /@nestjs/testing/8.2.6_b893ca8083ee374883b6d648098a9aeb: + /@nestjs/schematics/8.0.5_zkb6ykccqtyv52xru36vc75nne: + resolution: {integrity: sha512-nK1hWQeLNbdhsiJDX/XJXLqq7nC6/xxC8CN+seFTQmly+H3gG2xaFnl6JPHURumuQaYJX8JEpC8m0+4tz+wvOg==} + peerDependencies: + typescript: ^3.4.5 || ^4.3.5 + dependencies: + '@angular-devkit/core': 13.0.2_chokidar@3.5.2 + '@angular-devkit/schematics': 13.0.2_chokidar@3.5.2 + fs-extra: 10.0.0 + jsonc-parser: 3.0.0 + pluralize: 8.0.0 + typescript: 4.5.4 + transitivePeerDependencies: + - chokidar + dev: true + + /@nestjs/testing/8.2.6_xcj4vaed5y3ura5w2zeatcu25m: resolution: {integrity: sha512-Cg8tM7yxlLDBO+CTNjk6X/UkxGRsprFc8gDHGEGesiE9wLErxNnslFugfUnYWJaSzmLZSqcFFig7mThc8VSxrw==} peerDependencies: '@nestjs/common': ^8.0.0 @@ -2453,13 +2460,13 @@ packages: optional: true dependencies: optional: 0.1.4 - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca - '@nestjs/core': 8.2.6_2d4ee36c4446df4873bb38fb1c4583da - '@nestjs/platform-express': 8.2.6_732a54a2558f64827b8cc4f9baac4f1f + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi + '@nestjs/core': 8.2.6_fvhog3cei3puq453hd5ryrmd3i + '@nestjs/platform-express': 8.2.6_omvfjisvr5sie64myt43vlcpd4 tslib: 2.3.1 dev: true - /@nestjs/typeorm/8.0.3_7d295ee70f5e60cc7bb984ed65836a4c: + /@nestjs/typeorm/8.0.3_puuv5zyplzqmy65zqtwwla3kjq: resolution: {integrity: sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g==} peerDependencies: '@nestjs/common': ^8.0.0 @@ -2468,8 +2475,8 @@ packages: rxjs: ^7.2.0 typeorm: ^0.2.34 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca - '@nestjs/core': 8.2.6_2d4ee36c4446df4873bb38fb1c4583da + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi + '@nestjs/core': 8.2.6_fvhog3cei3puq453hd5ryrmd3i reflect-metadata: 0.1.13 rxjs: 7.5.2 typeorm: 0.2.41_ioredis@5.0.1+mysql2@2.3.3 @@ -2607,13 +2614,12 @@ packages: node-fetch: 2.6.7 transitivePeerDependencies: - encoding - dev: false /@popperjs/core/2.11.2: resolution: {integrity: sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==} dev: false - /@rollup/plugin-babel/5.3.1_04044e04815e91b52200805e849584d4: + /@rollup/plugin-babel/5.3.1_aqce4bebl2i3kiqaqbpijfme2q: resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} engines: {node: '>= 10.0.0'} peerDependencies: @@ -2711,7 +2717,7 @@ packages: prosemirror-view: 1.23.6 dev: false - /@tiptap/extension-blockquote/2.0.0-beta.26_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-blockquote/2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-A6yjcYovONJfOjQFk6vDYXswaCdCtCwjL7w9VTB0R2DLTuJvvRt9DWN0IDcMrj5G+aMgDq4GUUTitv+2Y8krDg==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2719,7 +2725,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-bold/2.0.0-beta.25_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-bold/2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-ZNdgFYDxKo8lAp0Pqzu45I0JH3ah8/X5TCYg9zNg3QwLUFT16g2LlWDMUDGT5pH9aXxgtFaEdoVacu0EyhlPnQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2727,7 +2733,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-bubble-menu/2.0.0-beta.55_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-bubble-menu/2.0.0-beta.55_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-v32/QnwwRbepdbrho8mTYru1/XNW/rJi3Mjrgo3rrIs67R86aEPmhmdzD3QEQUJhAJkduuwdw8zElmVWqIJQ9w==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2738,7 +2744,7 @@ packages: tippy.js: 6.3.7 dev: false - /@tiptap/extension-bullet-list/2.0.0-beta.26_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-bullet-list/2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-1n5HV8gY1tLjPk4x48nva6SZlFHoPlRfF6pqSu9JcJxPO7FUSPxUokuz4swYNe0LRrtykfyNz44dUcxKVhoFow==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2746,13 +2752,13 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-code-block-lowlight/2.0.0-beta.68_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-code-block-lowlight/2.0.0-beta.68_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-927vw4DJ5pq3lYOdJbBrCjJZ/YsbsEzWkb7SwRsaztDXcgsF2z8gt6dy7onXMaqm5rDx6eNofLPdhUyLCswW8Q==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 dependencies: '@tiptap/core': 2.0.0-beta.171 - '@tiptap/extension-code-block': 2.0.0-beta.37_@tiptap+core@2.0.0-beta.171 + '@tiptap/extension-code-block': 2.0.0-beta.37_neatuoot4hdn6bibaypc754kfi '@types/lowlight': 0.0.3 lowlight: 1.20.0 prosemirror-model: 1.16.1 @@ -2760,7 +2766,7 @@ packages: prosemirror-view: 1.23.6 dev: false - /@tiptap/extension-code-block/2.0.0-beta.37_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-code-block/2.0.0-beta.37_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-mJAM+PHaNoKRYwM3D36lZ51/aoPxxvZNQn3UBnZ6G7l0ZJSgB3JvBEzqK6S8nNFeYIIxGwv4QF6vXe4MG9ie2g==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2769,7 +2775,7 @@ packages: prosemirror-state: 1.3.4 dev: false - /@tiptap/extension-code/2.0.0-beta.26_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-code/2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-QcFWdEFfbJ1n5UFFBD17QPPAJ3J5p/b7XV484u0shCzywO7aNPV32QeHy1z0eMoyZtCbOWf6hg/a7Ugv8IwpHw==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2777,17 +2783,17 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-color/2.0.0-beta.9_e32f4205d0966701340db1456434a591: + /@tiptap/extension-color/2.0.0-beta.9_4mxueboqsztqcnanwfcwinffse: resolution: {integrity: sha512-c8zcaNCdwUwbgrutfsG7LD9KH7ZvDVwKOZHbOL4gMSwdH9s+6r1ThRFLEbKgHIJlTa2jd96qoo+lVfj1Qwp7ww==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 '@tiptap/extension-text-style': ^2.0.0-beta.1 dependencies: '@tiptap/core': 2.0.0-beta.171 - '@tiptap/extension-text-style': 2.0.0-beta.23_@tiptap+core@2.0.0-beta.171 + '@tiptap/extension-text-style': 2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi dev: false - /@tiptap/extension-document/2.0.0-beta.15_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-document/2.0.0-beta.15_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-ypENC+xUYD5m2t+KOKNYqyXnanXd5fxyIyhR1qeEEwwQwMXGNrO3kCH6O4mIDCpy+/WqHvVay2tV5dVsXnvY8w==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2795,7 +2801,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-dropcursor/2.0.0-beta.25_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-dropcursor/2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-GYf5s6dkZtsDy+TEkrQK6kLbfbitG4qnk02D+FlhlJMI/Nnx8rYCRJbwEHDdqrfX7XwZzULMqqqHvzxZYrEeNg==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2805,7 +2811,7 @@ packages: prosemirror-dropcursor: 1.4.0 dev: false - /@tiptap/extension-floating-menu/2.0.0-beta.50_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-floating-menu/2.0.0-beta.50_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-aQu1HtthMIYEPylr6kzioLxMiObLbcgwx9xZzF03KwNnkjQLbjZOeJX2RwSYVpiVgtfPBGOm3N/br6NSYec4yQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2816,7 +2822,7 @@ packages: tippy.js: 6.3.7 dev: false - /@tiptap/extension-gapcursor/2.0.0-beta.34_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-gapcursor/2.0.0-beta.34_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-Vm8vMWWQ2kJcUOLfB5CEo5pYgyudI7JeeiZvX9ScPmUmgKVYhEpt3EAICY9pUYJ41aAVH35gZLXkUtsz2f9GHw==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2826,7 +2832,7 @@ packages: prosemirror-gapcursor: 1.2.1 dev: false - /@tiptap/extension-hard-break/2.0.0-beta.30_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-hard-break/2.0.0-beta.30_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-X9xj/S+CikrbIE7ccUFVwit5QHEbflnKVxod+4zPwr1cxogFbE9AyLZE2MpYdx3z9LcnTYYi9leBqFrP4T/Olw==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2834,7 +2840,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-heading/2.0.0-beta.26_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-heading/2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-nR6W/3rjnZH1Swo7tGBoYsmO6xMvu9MGq6jlm3WVHCB7B3CsrRvCkTwGjVIbKTaZC4bQfx5gvAUpQFvwuU+M5w==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2842,7 +2848,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-highlight/2.0.0-beta.33_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-highlight/2.0.0-beta.33_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-TXyMiCcY5a0w5UFax350xU+T79GKJw2XwJ6Punc6sY2RRRgKaEbN1ZF0JCdQhQvD1ooKImHzCRYR8Pldb0xgfg==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2850,7 +2856,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-history/2.0.0-beta.21_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-history/2.0.0-beta.21_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-0v8Cl30V4dsabdpspLdk+f+lMoIvLFlJN5WRxtc7RRZ5gfJVxPHwooIKdvC51brfh/oJtWFCNMRjhoz0fRaF9A==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2860,7 +2866,7 @@ packages: prosemirror-history: 1.2.0 dev: false - /@tiptap/extension-image/2.0.0-beta.25_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-image/2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-RgW5jFVS2QNDvFhBOz7H1hY6LjYcbVAa/mE4F4c3RPg3o7GJZXNoL9s+k0QkEM2GXAvY6fX+OICMBn8TSENXKA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2868,7 +2874,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-italic/2.0.0-beta.25_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-italic/2.0.0-beta.25_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-7PvhioTX9baVp5+AmmZU0qna+dFPZCRlSEN/GciH57N77d2uhJ/ZW5iQWTbvy5HBNddQB4Jts1UDIaC7WASrGA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2876,7 +2882,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-link/2.0.0-beta.36_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-link/2.0.0-beta.36_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-jV0EBM/QPfR4e5FG5OPHZARnYS+CL8yhCzHO4J1Nb1i/+vRY9QpPVBruZABBwt+J+PMdq6t/6vvIXejCR3wyAg==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2887,7 +2893,7 @@ packages: prosemirror-state: 1.3.4 dev: false - /@tiptap/extension-list-item/2.0.0-beta.20_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-list-item/2.0.0-beta.20_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-5IPEspJt38t9ROj4xLUesOVEYlTT/R9Skd9meHRxJQZX1qrzBICs5PC/WRIsnexrvTBhdxpYgCYjpvpsJBlKuQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2895,18 +2901,18 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-mention/2.0.0-beta.95_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-mention/2.0.0-beta.95_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-AiikYJa33APtMI7c6a4EpPhqAYxHHnub5b9hd62zM1peBp2D2aO1dEIL7cB7O2P8EBZ2pu2QN2mFcCbVlSi0Xw==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 dependencies: '@tiptap/core': 2.0.0-beta.171 - '@tiptap/suggestion': 2.0.0-beta.90_@tiptap+core@2.0.0-beta.171 + '@tiptap/suggestion': 2.0.0-beta.90_neatuoot4hdn6bibaypc754kfi prosemirror-model: 1.16.1 prosemirror-state: 1.3.4 dev: false - /@tiptap/extension-ordered-list/2.0.0-beta.27_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-ordered-list/2.0.0-beta.27_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-apFDeignxdZb3cA3p1HJu0zw1JgJdBYUBz1r7f99qdNybYuk3I/1MPUvlOuOgvIrBB/wydoyVDP+v9F7QN3tfQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2914,7 +2920,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-paragraph/2.0.0-beta.23_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-paragraph/2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-VWAxyzecErYWk97Kv/Gkghh97zAQTcaVOisEnYYArZAlyYDaYM48qVssAC/vnRRynP2eQxb1EkppbAxE+bMHAA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2922,7 +2928,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-placeholder/2.0.0-beta.47_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-placeholder/2.0.0-beta.47_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-+XCRwI022Gkay5STr4uQDCcXNq9RDq4adILoTN37rEA8RB5DSRi1eTOCwkQN9+rK7U46/BZECrUwC5NBi551eQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2933,7 +2939,7 @@ packages: prosemirror-view: 1.23.6 dev: false - /@tiptap/extension-strike/2.0.0-beta.27_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-strike/2.0.0-beta.27_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-2dmCgtesuDdivM/54Q+Y6Tc3JbGz1SkHP6c62piuqBiYLWg3xa16zChZOhfN8szbbQlBgLT6XRTDt3c2Ux+Dug==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2941,7 +2947,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-subscript/2.0.0-beta.10_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-subscript/2.0.0-beta.10_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-er8/1lp0Rb+SKwEioW0w4oVf3EkdQZ0WS/5kPBG4W0DncfUMT+bw5de76S3kRL9PLZ9UShAL7wuXtuiSi5QsMw==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2949,7 +2955,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-superscript/2.0.0-beta.10_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-superscript/2.0.0-beta.10_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-TUUBS8XsD2MorGORYVlhGDH7wcc9diSbHscD4Dnz8pKWVR0JPUd/od4h5qSffDzAOKxtphTiX9LOFWk6zVooKg==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2957,7 +2963,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-table-cell/2.0.0-beta.20_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-table-cell/2.0.0-beta.20_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-IllQyxLQvgm1FAewz3U+DkgNHRthmuVrtUQnG6la45qdUOLCOrpFbRRaQ1LJ/BpbvZ2Xs1o2yAa97BqZOPwovQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2965,7 +2971,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-table-header/2.0.0-beta.22_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-table-header/2.0.0-beta.22_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-nMrghrfl+ZS4EDixs3lgXnHw1Q+ECyTugpRvS36rP7b8GFp3GXm9DfbIAUzwGGfcq1D7DwRnJUDM6ARdWXyw0w==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2973,7 +2979,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-table-row/2.0.0-beta.19_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-table-row/2.0.0-beta.19_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-ldEVDpIUX7ZqbViTy4c/RfyNGRv++O/r3A/Ivuon1PykaDDTbPlp5JM89FunAD39cLAbo2HKtweqdmzCMlZsqA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2981,7 +2987,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-table/2.0.0-beta.48_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-table/2.0.0-beta.48_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-Hcx3kOBQyazQ3dV0Oq4zKIl1og4EqUuZ5nEWxwcb8HgxSUYIhAJQ7pujPZiRLfkoFy92oVwmh9KhBRfQqRkUpQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2991,7 +2997,7 @@ packages: prosemirror-view: 1.23.6 dev: false - /@tiptap/extension-task-item/2.0.0-beta.31_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-task-item/2.0.0-beta.31_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-9MCInLAf/l/wDD1N3GgOImemloFARi1l9AJ5acfo+sDjN52yfvaLb//lvLJ6IGz4xGepeAyCME8Qns8UGqG4RQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -2999,7 +3005,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-task-list/2.0.0-beta.26_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-task-list/2.0.0-beta.26_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-7zPpz9eOUCnFyWNDFYPCUJ39gjID+mCI5BuXyXrjJjDfm8wxg/xTgg9+KC6xakczos7DypnhzlRKSs4EFczeUg==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3007,7 +3013,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-text-align/2.0.0-beta.29_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-text-align/2.0.0-beta.29_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-FNGpl0tVtgG7AK9kVWF/+CGYHta05NpoME4/j6+vhNlZLBNXRA+AKg7W5T8UxmtaC9yGoJsBs2X8M9eCxWVaEQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3015,7 +3021,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-text-style/2.0.0-beta.23_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-text-style/2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-h/7o//RB4WRrLKWV7E5eFk7tZnfjH0Wt9klixOmvTmus6dm00a7r6wTuaT1GNjfPOgClP3K185lTA5rrdgrxRA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3023,7 +3029,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-text/2.0.0-beta.15_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-text/2.0.0-beta.15_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-S3j2+HyV2gsXZP8Wg/HA+YVXQsZ3nrXgBM9HmGAxB0ESOO50l7LWfip0f3qcw1oRlh5H3iLPkA6/f7clD2/TFA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3031,7 +3037,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/extension-underline/2.0.0-beta.23_@tiptap+core@2.0.0-beta.171: + /@tiptap/extension-underline/2.0.0-beta.23_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-pMjFH/NpFWLd2XQQa5rG9rGVQ9mu3ygdtu6VGfJ3aAjzBiyLXDKhE4biIFWyFsr8zLpp7DjwbrmLV0UGvbG1WQ==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3039,7 +3045,7 @@ packages: '@tiptap/core': 2.0.0-beta.171 dev: false - /@tiptap/react/2.0.0-beta.107_a3fcdb91535fe17b69dfabaa94f3bb3d: + /@tiptap/react/2.0.0-beta.107_up6nxektl7qxw2o7vovjj453hu: resolution: {integrity: sha512-4G14F32TxEuYNYyOOQQxJ3ddFooPOv9Opcw5mjKxgKFZLzlBMTBl7os9ndrMfVdWYTI3RpZlfiAQxq3QlHamxA==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3047,14 +3053,14 @@ packages: react-dom: ^17.0.0 dependencies: '@tiptap/core': 2.0.0-beta.171 - '@tiptap/extension-bubble-menu': 2.0.0-beta.55_@tiptap+core@2.0.0-beta.171 - '@tiptap/extension-floating-menu': 2.0.0-beta.50_@tiptap+core@2.0.0-beta.171 + '@tiptap/extension-bubble-menu': 2.0.0-beta.55_neatuoot4hdn6bibaypc754kfi + '@tiptap/extension-floating-menu': 2.0.0-beta.50_neatuoot4hdn6bibaypc754kfi prosemirror-view: 1.23.6 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 dev: false - /@tiptap/suggestion/2.0.0-beta.90_@tiptap+core@2.0.0-beta.171: + /@tiptap/suggestion/2.0.0-beta.90_neatuoot4hdn6bibaypc754kfi: resolution: {integrity: sha512-L5PPYRatY/75uJJRQx2o/Ce+gzcOkmd81TwLjio9sADV3bRf4DO4WYcQy0AtGe6uNSz78DTL0SUVw4204VjoBw==} peerDependencies: '@tiptap/core': ^2.0.0-beta.1 @@ -3451,7 +3457,7 @@ packages: resolution: {integrity: sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==} dev: false - /@typescript-eslint/eslint-plugin/5.21.0_19515efd875c7ffcd67055d8be736b9f: + /@typescript-eslint/eslint-plugin/5.21.0_dfiv57mhlr77zvtqkxml443lt4: resolution: {integrity: sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3462,10 +3468,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.21.0_eslint@8.14.0+typescript@4.5.5 + '@typescript-eslint/parser': 5.21.0_sdq423wcawycjzugemfabbjutq '@typescript-eslint/scope-manager': 5.21.0 - '@typescript-eslint/type-utils': 5.21.0_eslint@8.14.0+typescript@4.5.5 - '@typescript-eslint/utils': 5.21.0_eslint@8.14.0+typescript@4.5.5 + '@typescript-eslint/type-utils': 5.21.0_sdq423wcawycjzugemfabbjutq + '@typescript-eslint/utils': 5.21.0_sdq423wcawycjzugemfabbjutq debug: 4.3.4 eslint: 8.14.0 functional-red-black-tree: 1.0.1 @@ -3478,7 +3484,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.21.0_eslint@8.14.0+typescript@4.5.5: + /@typescript-eslint/parser/5.21.0_sdq423wcawycjzugemfabbjutq: resolution: {integrity: sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3506,7 +3512,7 @@ packages: '@typescript-eslint/visitor-keys': 5.21.0 dev: true - /@typescript-eslint/type-utils/5.21.0_eslint@8.14.0+typescript@4.5.5: + /@typescript-eslint/type-utils/5.21.0_sdq423wcawycjzugemfabbjutq: resolution: {integrity: sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3516,7 +3522,7 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.21.0_eslint@8.14.0+typescript@4.5.5 + '@typescript-eslint/utils': 5.21.0_sdq423wcawycjzugemfabbjutq debug: 4.3.4 eslint: 8.14.0 tsutils: 3.21.0_typescript@4.5.5 @@ -3551,7 +3557,7 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.21.0_eslint@8.14.0+typescript@4.5.5: + /@typescript-eslint/utils/5.21.0_sdq423wcawycjzugemfabbjutq: resolution: {integrity: sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3701,7 +3707,6 @@ packages: dependencies: mime-types: 2.1.34 negotiator: 0.6.2 - dev: false /acorn-globals/6.0.0: resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} @@ -3746,7 +3751,6 @@ packages: resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==} engines: {node: '>=0.4.0'} hasBin: true - dev: true /address/1.1.2: resolution: {integrity: sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==} @@ -3939,7 +3943,6 @@ packages: /append-field/1.0.0: resolution: {integrity: sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=} - dev: false /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3966,7 +3969,6 @@ packages: /array-flatten/1.1.1: resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} - dev: false /array-includes/3.1.4: resolution: {integrity: sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==} @@ -4095,7 +4097,6 @@ packages: follow-redirects: 1.14.7 transitivePeerDependencies: - debug - dev: false /axios/0.25.0: resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} @@ -4292,7 +4293,8 @@ packages: qs: 6.9.6 raw-body: 2.4.2 type-is: 1.6.18 - dev: false + transitivePeerDependencies: + - supports-color /body-parser/1.20.0: resolution: {integrity: sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==} @@ -4310,6 +4312,8 @@ packages: raw-body: 2.5.1 type-is: 1.6.18 unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color dev: false /bowser/1.9.4: @@ -4430,7 +4434,6 @@ packages: dependencies: dicer: 0.2.5 readable-stream: 1.1.14 - dev: false /bytes/3.0.0: resolution: {integrity: sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=} @@ -4440,7 +4443,6 @@ packages: /bytes/3.1.1: resolution: {integrity: sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==} engines: {node: '>= 0.8'} - dev: false /bytes/3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -4553,14 +4555,12 @@ packages: /class-transformer/0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} - dev: false /class-validator/0.13.2: resolution: {integrity: sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==} dependencies: libphonenumber-js: 1.9.46 validator: 13.7.0 - dev: false /classnames/2.3.1: resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==} @@ -4765,6 +4765,8 @@ packages: on-headers: 1.0.2 safe-buffer: 5.1.2 vary: 1.1.2 + transitivePeerDependencies: + - supports-color dev: false /compute-scroll-into-view/1.0.17: @@ -4782,7 +4784,6 @@ packages: inherits: 2.0.4 readable-stream: 2.3.7 typedarray: 0.0.6 - dev: false /concurrently/7.0.0: resolution: {integrity: sha512-WKM7PUsI8wyXpF80H+zjHP32fsgsHNQfPLw/e70Z5dYkV7hF+rf8q3D+ScWJIEr57CpkO3OWBko6hwhQLPR8Pw==} @@ -4818,19 +4819,16 @@ packages: /consola/2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} - dev: false /content-disposition/0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.2.1 - dev: false /content-type/1.0.4: resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} engines: {node: '>= 0.6'} - dev: false /convert-source-map/1.8.0: resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} @@ -4847,12 +4845,10 @@ packages: /cookie-signature/1.0.6: resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=} - dev: false /cookie/0.4.1: resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} engines: {node: '>= 0.6'} - dev: false /cookiejar/2.1.3: resolution: {integrity: sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==} @@ -4899,7 +4895,6 @@ packages: /core-util-is/1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: false /cors/2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} @@ -4907,7 +4902,6 @@ packages: dependencies: object-assign: 4.1.1 vary: 1.1.2 - dev: false /cos-nodejs-sdk-v5/2.11.9: resolution: {integrity: sha512-szsUw/8hx1RWUfMNwgErzYcdPM3EwcmgbylqQf82HPZALMCAcaa7qCeAxVQHNvCumWYeQLy7EEloZjMUyjg7Ug==} @@ -5071,11 +5065,21 @@ packages: /debug/2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.0.0 /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.1.3 dev: true @@ -5217,7 +5221,6 @@ packages: /depd/1.1.2: resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=} engines: {node: '>= 0.6'} - dev: false /depd/2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} @@ -5226,7 +5229,6 @@ packages: /destroy/1.0.4: resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=} - dev: false /destroy/1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} @@ -5255,7 +5257,6 @@ packages: dependencies: readable-stream: 1.1.14 streamsearch: 0.1.2 - dev: false /diff-sequences/27.4.0: resolution: {integrity: sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==} @@ -5369,7 +5370,6 @@ packages: /ee-first/1.1.1: resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} - dev: false /ejs/3.1.8: resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} @@ -5406,7 +5406,6 @@ packages: /encodeurl/1.0.2: resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} engines: {node: '>= 0.8'} - dev: false /end-of-stream/1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -5528,7 +5527,6 @@ packages: /escape-html/1.0.3: resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=} - dev: false /escape-string-regexp/1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} @@ -5584,29 +5582,54 @@ packages: dependencies: debug: 3.2.7 resolve: 1.22.0 + transitivePeerDependencies: + - supports-color dev: true - /eslint-module-utils/2.7.3: + /eslint-module-utils/2.7.3_siwxndurugrzrndocd3gqxwhna: resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==} engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true dependencies: + '@typescript-eslint/parser': 5.21.0_sdq423wcawycjzugemfabbjutq debug: 3.2.7 + eslint-import-resolver-node: 0.3.6 find-up: 2.1.0 + transitivePeerDependencies: + - supports-color dev: true - /eslint-plugin-import/2.26.0_eslint@8.14.0: + /eslint-plugin-import/2.26.0_5yvnzvdwzksrnum37j3gumwy4y: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: + '@typescript-eslint/parser': '*' eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true dependencies: + '@typescript-eslint/parser': 5.21.0_sdq423wcawycjzugemfabbjutq array-includes: 3.1.4 array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 eslint: 8.14.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.3 + eslint-module-utils: 2.7.3_siwxndurugrzrndocd3gqxwhna has: 1.0.3 is-core-module: 2.8.1 is-glob: 4.0.3 @@ -5614,9 +5637,13 @@ packages: object.values: 1.1.5 resolve: 1.22.0 tsconfig-paths: 3.14.1 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color dev: true - /eslint-plugin-prettier/4.0.0_740be41c8168d0cc214a306089357ad0: + /eslint-plugin-prettier/4.0.0_oqf6ihebndimyikkgbqisnl22a: resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} engines: {node: '>=6.0.0'} peerDependencies: @@ -5633,7 +5660,7 @@ packages: prettier-linter-helpers: 1.0.0 dev: true - /eslint-plugin-prettier/4.0.0_74ebb802163a9b4fa8f89d76ed02f62a: + /eslint-plugin-prettier/4.0.0_otv3qaqwhknu7khytv3o2axwfi: resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} engines: {node: '>=6.0.0'} peerDependencies: @@ -5817,7 +5844,6 @@ packages: /etag/1.8.1: resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=} engines: {node: '>= 0.6'} - dev: false /events/3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} @@ -5919,7 +5945,8 @@ packages: type-is: 1.6.18 utils-merge: 1.0.1 vary: 1.1.2 - dev: false + transitivePeerDependencies: + - supports-color /extend-shallow/2.0.1: resolution: {integrity: sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=} @@ -6062,7 +6089,8 @@ packages: parseurl: 1.3.3 statuses: 1.5.0 unpipe: 1.0.0 - dev: false + transitivePeerDependencies: + - supports-color /find-cache-dir/3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} @@ -6114,7 +6142,6 @@ packages: peerDependenciesMeta: debug: optional: true - dev: false /foreach/2.0.5: resolution: {integrity: sha1-C+4AUBiusmDQo6865ljdATbsG5k=} @@ -6124,7 +6151,7 @@ packages: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} dev: false - /fork-ts-checker-webpack-plugin/6.5.0_787dd39517260957bc59f00cd4915d0b: + /fork-ts-checker-webpack-plugin/6.5.0_pb65hfixeyevppcz6agnjek5bm: resolution: {integrity: sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: @@ -6208,12 +6235,10 @@ packages: /forwarded/0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - dev: false /fresh/0.5.2: resolution: {integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=} engines: {node: '>= 0.6'} - dev: false /fs-extra/10.0.0: resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==} @@ -6547,7 +6572,6 @@ packages: setprototypeof: 1.2.0 statuses: 1.5.0 toidentifier: 1.0.1 - dev: false /http-errors/2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} @@ -6778,7 +6802,6 @@ packages: /ipaddr.js/1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - dev: false /is-arguments/1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} @@ -7023,11 +7046,9 @@ packages: /isarray/0.0.1: resolution: {integrity: sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=} - dev: false /isarray/1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: false /isarray/2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -7093,7 +7114,6 @@ packages: /iterare/1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} - dev: false /jake/10.8.5: resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} @@ -7203,7 +7223,7 @@ packages: micromatch: 4.0.4 pretty-format: 27.4.6 slash: 3.0.0 - ts-node: 10.4.0_06de4b00c69b73d094e2c5b522a6ad57 + ts-node: 10.4.0_a3pewaggtnz5bfhcyw2sfjvnk4 transitivePeerDependencies: - bufferutil - canvas @@ -7876,7 +7896,6 @@ packages: /libphonenumber-js/1.9.46: resolution: {integrity: sha512-QqTX4UVsGy24njtCgLRspiKpxfRniRBZE/P+d0vQXuYWQ+hwDS6X0ouo0O/SRyf7bhhMCE71b6vAvLMtY5PfEw==} - dev: false /lie/3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -8208,7 +8227,6 @@ packages: /media-typer/0.3.0: resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} engines: {node: '>= 0.6'} - dev: false /memfs/3.4.1: resolution: {integrity: sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==} @@ -8245,7 +8263,6 @@ packages: /merge-descriptors/1.0.1: resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=} - dev: false /merge-refs/1.0.0: resolution: {integrity: sha512-WZ4S5wqD9FCR9hxkLgvcHJCBxzXzy3VVE6p8W2OzxRzB+hLRlcadGE2bW9xp2KSzk10rvp4y+pwwKO6JQVguMg==} @@ -8287,7 +8304,6 @@ packages: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} hasBin: true - dev: false /mime/2.6.0: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} @@ -8345,7 +8361,6 @@ packages: hasBin: true dependencies: minimist: 1.2.6 - dev: false /mkdirp/1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} @@ -8379,7 +8394,6 @@ packages: on-finished: 2.3.0 type-is: 1.6.18 xtend: 4.0.2 - dev: false /mute-stream/0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -8443,12 +8457,11 @@ packages: /negotiator/0.6.2: resolution: {integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==} engines: {node: '>= 0.6'} - dev: false /neo-async/2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - /nestjs-pino/2.5.2_61f1f701bb68c430ae2e1a4fec1d7a2d: + /nestjs-pino/2.5.2_mhy7oan3ndcdblrodjh6yhl2fu: resolution: {integrity: sha512-CJDiHRLJTucpgi5blY98Hh+2lj21fhKksyVR3R7ObOhM+/9tf+kmX02LuIL/eot+oYEPzLhHuTqVegL/kiDk6g==} engines: {node: '>=12.17.0 || >=13.10.0'} requiresBuild: true @@ -8456,7 +8469,7 @@ packages: '@nestjs/common': ^8.0.0 pino-http: ^6.4.0 dependencies: - '@nestjs/common': 8.2.6_e2dd7264172553abc41c1a07952dd8ca + '@nestjs/common': 8.2.6_4loxezaxevj2xra4didzkloyzi pino-http: 7.0.0 dev: false @@ -8473,7 +8486,7 @@ packages: babel-loader: 8.2.5 clean-webpack-plugin: 4.0.0 globby: 11.1.0 - next: 12.1.0_react-dom@17.0.2+react@17.0.2 + next: 12.1.0_sfoxds7t5ydpegc3knd667wn6m terser-webpack-plugin: 5.3.1 workbox-webpack-plugin: 6.5.3 workbox-window: 6.5.3 @@ -8481,14 +8494,13 @@ packages: - '@babel/core' - '@swc/core' - '@types/babel__core' - - acorn - esbuild - supports-color - uglify-js - webpack dev: false - /next/12.1.0_react-dom@17.0.2+react@17.0.2: + /next/12.1.0_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==} engines: {node: '>=12.22.0'} hasBin: true @@ -8546,7 +8558,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: false /node-int64/0.4.0: resolution: {integrity: sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=} @@ -8559,6 +8570,11 @@ packages: resolution: {integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==} dev: false + /nodemailer/6.7.5: + resolution: {integrity: sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg==} + engines: {node: '>=6.0.0'} + dev: false + /normalize-package-data/2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -8613,7 +8629,6 @@ packages: /object-hash/2.2.0: resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} engines: {node: '>= 6'} - dev: false /object-inspect/1.12.0: resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} @@ -8686,7 +8701,6 @@ packages: engines: {node: '>= 0.8'} dependencies: ee-first: 1.1.1 - dev: false /on-finished/2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} @@ -8904,7 +8918,6 @@ packages: /parseurl/1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - dev: false /passport-jwt/4.0.0: resolution: {integrity: sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==} @@ -8951,11 +8964,9 @@ packages: /path-to-regexp/0.1.7: resolution: {integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=} - dev: false /path-to-regexp/3.2.0: resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} - dev: false /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -9248,7 +9259,6 @@ packages: /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: false /process-warning/1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} @@ -9269,7 +9279,7 @@ packages: object-assign: 4.1.1 react-is: 16.13.1 - /prosemirror-codemark/0.3.6_4cf2d6daa1edf7cc132d3e7b74beaf4d: + /prosemirror-codemark/0.3.6_jtznnwvb5x34yeznhz5xjpvpju: resolution: {integrity: sha512-uLUvIGfIEFdWEOR/wR2XgnccnyaUYNV/Degv/Dd5t8j1qKdbWbSkfqHgGi7sp5CcuBoZ/Q2hM6wbMc558T/Iag==} peerDependencies: prosemirror-inputrules: ^1.1.3 @@ -9365,7 +9375,7 @@ packages: prosemirror-model: 1.16.1 dev: false - /prosemirror-utils/0.9.6_4f14324fc97f6af3c95cc9ca12f2bcb1: + /prosemirror-utils/0.9.6_j4kdet6jp5vphsk4zhfbf4v4we: resolution: {integrity: sha512-UC+j9hQQ1POYfMc5p7UFxBTptRiGPR7Kkmbl3jVvU8VgQbkI89tR/GK+3QYC8n+VvBZrtAoCrJItNhWSxX3slA==} peerDependencies: prosemirror-model: ^1.0.0 @@ -9391,7 +9401,6 @@ packages: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - dev: false /proxy-agent/5.0.0: resolution: {integrity: sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==} @@ -9453,7 +9462,6 @@ packages: /qs/6.9.6: resolution: {integrity: sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==} engines: {node: '>=0.6'} - dev: false /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9475,7 +9483,6 @@ packages: /range-parser/1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - dev: false /raw-body/2.4.2: resolution: {integrity: sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==} @@ -9485,7 +9492,6 @@ packages: http-errors: 1.8.1 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: false /raw-body/2.5.1: resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} @@ -9497,7 +9503,7 @@ packages: unpipe: 1.0.0 dev: false - /react-countdown/2.3.2_react-dom@17.0.2+react@17.0.2: + /react-countdown/2.3.2_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-Q4SADotHtgOxNWhDdvgupmKVL0pMB9DvoFcxv5AzjsxVhzOVxnttMbAywgqeOdruwEAmnPhOhNv/awAgkwru2w==} peerDependencies: react: '>= 15' @@ -9519,7 +9525,7 @@ packages: scheduler: 0.20.2 dev: false - /react-draggable/4.4.4_react-dom@17.0.2+react@17.0.2: + /react-draggable/4.4.4_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==} peerDependencies: react: '>= 16.3.0' @@ -9554,7 +9560,7 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true - /react-lazy-load-image-component/1.5.4_react-dom@17.0.2+react@17.0.2: + /react-lazy-load-image-component/1.5.4_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-PSi9tckzZmiqfYZwS6ke2RoRbICsN5m0qsG6fEjUdQNe5STiJieXLlRuGD3uAASOQPFiKYFSLoueN07nk0uffw==} peerDependencies: react: ^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x @@ -9570,7 +9576,7 @@ packages: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} dev: false - /react-pdf/5.7.2_react-dom@17.0.2+react@17.0.2: + /react-pdf/5.7.2_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-hdDwvf007V0i2rPCqQVS1fa70CXut17SN3laJYlRHzuqcu8sLLjEoeXihty6c0Ev5g1mw31b8OT8EwRw1s8C4g==} peerDependencies: react: ^16.3.0 || ^17.0.0 || ^18.0.0 @@ -9593,7 +9599,7 @@ packages: - worker-loader dev: false - /react-query/3.39.0_react-dom@17.0.2+react@17.0.2: + /react-query/3.39.0_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-Od0IkSuS79WJOhzWBx/ys0x13+7wFqgnn64vBqqAAnZ9whocVhl/y1padD5uuZ6EIkXbFbInax0qvY7zGM0thA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -9612,7 +9618,7 @@ packages: react-dom: 17.0.2_react@17.0.2 dev: false - /react-resizable/1.11.1_react-dom@17.0.2+react@17.0.2: + /react-resizable/1.11.1_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-S70gbLaAYqjuAd49utRHibtHLrHXInh7GuOR+6OO6RO6uleQfuBnWmZjRABfqNEx3C3Z6VPLg0/0uOYFrkfu9Q==} peerDependencies: react: 0.14.x || 15.x || 16.x || 17.x @@ -9621,7 +9627,7 @@ packages: prop-types: 15.8.1 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 - react-draggable: 4.4.4_react-dom@17.0.2+react@17.0.2 + react-draggable: 4.4.4_sfoxds7t5ydpegc3knd667wn6m dev: false /react-side-effect/2.1.1_react@17.0.2: @@ -9632,7 +9638,7 @@ packages: react: 17.0.2 dev: false - /react-sortable-hoc/1.11.0_react-dom@17.0.2+react@17.0.2: + /react-sortable-hoc/1.11.0_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==} peerDependencies: react: ^0.14.0 || ^15.0.0 || ^16.0.0 @@ -9645,7 +9651,7 @@ packages: react-dom: 17.0.2_react@17.0.2 dev: false - /react-split-pane/0.1.92_react-dom@17.0.2+react@17.0.2: + /react-split-pane/0.1.92_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-GfXP1xSzLMcLJI5BM36Vh7GgZBpy+U/X0no+VM3fxayv+p1Jly5HpMofZJraeaMl73b3hvlr+N9zJKvLB/uz9w==} peerDependencies: react: ^16.0.0-0 @@ -9664,7 +9670,7 @@ packages: prop-types: 15.8.1 dev: false - /react-visibility-sensor/5.1.1_react-dom@17.0.2+react@17.0.2: + /react-visibility-sensor/5.1.1_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-cTUHqIK+zDYpeK19rzW6zF9YfT4486TIgizZW53wEZ+/GPBbK7cNS0EHyJVyHYacwFEvvHLEKfgJndbemWhB/w==} peerDependencies: react: '>=16.0.0' @@ -9675,7 +9681,7 @@ packages: react-dom: 17.0.2_react@17.0.2 dev: false - /react-window/1.8.6_react-dom@17.0.2+react@17.0.2: + /react-window/1.8.6_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==} engines: {node: '>8.0.0'} peerDependencies: @@ -9722,7 +9728,6 @@ packages: inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 - dev: false /readable-stream/2.3.7: resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} @@ -9734,7 +9739,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: false /readable-stream/3.6.0: resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} @@ -9784,7 +9788,6 @@ packages: /reflect-metadata/0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} - dev: false /regenerate-unicode-properties/10.0.1: resolution: {integrity: sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==} @@ -9964,8 +9967,6 @@ packages: rollup: 2.74.1 serialize-javascript: 4.0.0 terser: 5.10.0 - transitivePeerDependencies: - - acorn dev: false /rollup/2.74.1: @@ -10165,7 +10166,8 @@ packages: on-finished: 2.3.0 range-parser: 1.2.1 statuses: 1.5.0 - dev: false + transitivePeerDependencies: + - supports-color /seq-queue/0.0.5: resolution: {integrity: sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=} @@ -10190,7 +10192,8 @@ packages: escape-html: 1.0.3 parseurl: 1.3.3 send: 0.17.2 - dev: false + transitivePeerDependencies: + - supports-color /setimmediate/1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -10198,7 +10201,6 @@ packages: /setprototypeof/1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: false /sha.js/2.4.11: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} @@ -10427,7 +10429,6 @@ packages: /statuses/1.5.0: resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=} engines: {node: '>= 0.6'} - dev: false /statuses/2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} @@ -10456,7 +10457,6 @@ packages: /streamsearch/0.1.2: resolution: {integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=} engines: {node: '>=0.8.0'} - dev: false /string-argv/0.3.1: resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} @@ -10514,13 +10514,11 @@ packages: /string_decoder/0.10.31: resolution: {integrity: sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=} - dev: false /string_decoder/1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: false /string_decoder/1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -10699,7 +10697,7 @@ packages: stylelint: 14.6.1 dev: true - /stylelint-prettier/2.0.0_prettier@2.5.1+stylelint@14.6.1: + /stylelint-prettier/2.0.0_x3dt43yf7vqctazx2b26xrlr7q: resolution: {integrity: sha512-jvT3G+9lopkeB0ARmDPszyfaOnvnIF+30QCjZxyt7E6fynI1T9mOKgYDNb9bXX17M7PXMZaX3j/26wqakjp1tw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -10894,7 +10892,7 @@ packages: supports-hyperlinks: 2.2.0 dev: true - /terser-webpack-plugin/5.3.0_acorn@8.7.0+webpack@5.66.0: + /terser-webpack-plugin/5.3.0_webpack@5.66.0: resolution: {integrity: sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -10914,10 +10912,8 @@ packages: schema-utils: 3.1.1 serialize-javascript: 6.0.0 source-map: 0.6.1 - terser: 5.10.0_acorn@8.7.0 + terser: 5.10.0 webpack: 5.66.0 - transitivePeerDependencies: - - acorn dev: true /terser-webpack-plugin/5.3.1: @@ -10941,31 +10937,12 @@ packages: serialize-javascript: 6.0.0 source-map: 0.6.1 terser: 5.10.0 - transitivePeerDependencies: - - acorn dev: false /terser/5.10.0: resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==} engines: {node: '>=10'} hasBin: true - peerDependencies: - acorn: ^8.5.0 - peerDependenciesMeta: - acorn: - optional: true - dependencies: - commander: 2.20.3 - source-map: 0.7.3 - source-map-support: 0.5.21 - dev: false - - /terser/5.10.0_acorn@8.7.0: - resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==} - engines: {node: '>=10'} - hasBin: true - peerDependencies: - acorn: ^8.5.0 peerDependenciesMeta: acorn: optional: true @@ -10974,7 +10951,6 @@ packages: commander: 2.20.3 source-map: 0.7.3 source-map-support: 0.5.21 - dev: true /test-exclude/6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} @@ -11065,7 +11041,6 @@ packages: /toidentifier/1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - dev: false /tough-cookie/2.5.0: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} @@ -11086,7 +11061,6 @@ packages: /tr46/0.0.3: resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=} - dev: false /tr46/1.0.1: resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=} @@ -11110,7 +11084,7 @@ packages: engines: {node: '>=8'} dev: true - /ts-jest/27.1.3_bc464af956686ccd28ad646067711a2c: + /ts-jest/27.1.3_xrdev6kwnbwm2kfnmrqgo4i2fq: resolution: {integrity: sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -11158,7 +11132,7 @@ packages: typescript: 4.5.5 dev: true - /ts-node/10.4.0_06de4b00c69b73d094e2c5b522a6ad57: + /ts-node/10.4.0_a3pewaggtnz5bfhcyw2sfjvnk4: resolution: {integrity: sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==} hasBin: true peerDependencies: @@ -11294,7 +11268,6 @@ packages: dependencies: media-typer: 0.3.0 mime-types: 2.1.34 - dev: false /typedarray-to-buffer/3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} @@ -11304,7 +11277,6 @@ packages: /typedarray/0.0.6: resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=} - dev: false /typeorm/0.2.41_ioredis@5.0.1+mysql2@2.3.3: resolution: {integrity: sha512-/d8CLJJxKPgsnrZWiMyPI0rz2MFZnBQrnQ5XP3Vu3mswv2WPexb58QM6BEtmRmlTMYN5KFWUz8SKluze+wS9xw==} @@ -11462,7 +11434,6 @@ packages: /unpipe/1.0.0: resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} engines: {node: '>= 0.8'} - dev: false /upath/1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} @@ -11535,7 +11506,6 @@ packages: /utils-merge/1.0.1: resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} engines: {node: '>= 0.4.0'} - dev: false /uuid/3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} @@ -11546,7 +11516,6 @@ packages: /uuid/8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - dev: false /v8-compile-cache/2.3.0: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} @@ -11571,12 +11540,10 @@ packages: /validator/13.7.0: resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} engines: {node: '>= 0.10'} - dev: false /vary/1.1.2: resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} engines: {node: '>= 0.8'} - dev: false /verror/1.10.0: resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=} @@ -11636,7 +11603,6 @@ packages: /webidl-conversions/3.0.1: resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=} - dev: false /webidl-conversions/4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -11700,7 +11666,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.1.1 tapable: 2.2.1 - terser-webpack-plugin: 5.3.0_acorn@8.7.0+webpack@5.66.0 + terser-webpack-plugin: 5.3.0_webpack@5.66.0 watchpack: 2.3.1 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -11724,7 +11690,6 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: false /whatwg-url/7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -11826,7 +11791,7 @@ packages: '@babel/core': 7.16.12 '@babel/preset-env': 7.17.12_@babel+core@7.16.12 '@babel/runtime': 7.16.7 - '@rollup/plugin-babel': 5.3.1_04044e04815e91b52200805e849584d4 + '@rollup/plugin-babel': 5.3.1_aqce4bebl2i3kiqaqbpijfme2q '@rollup/plugin-node-resolve': 11.2.1_rollup@2.74.1 '@rollup/plugin-replace': 2.4.2_rollup@2.74.1 '@surma/rollup-plugin-off-main-thread': 2.2.3 @@ -11861,7 +11826,6 @@ packages: workbox-window: 6.5.3 transitivePeerDependencies: - '@types/babel__core' - - acorn - supports-color dev: false @@ -11958,7 +11922,6 @@ packages: workbox-build: 6.5.3 transitivePeerDependencies: - '@types/babel__core' - - acorn - supports-color dev: false @@ -12071,9 +12034,8 @@ packages: /xtend/4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} - dev: false - /y-prosemirror/1.0.14_8fd72c89aecefb95d86a797b5207d945: + /y-prosemirror/1.0.14_r7lszcnoz35zlwdkpf5veb6ziu: resolution: {integrity: sha512-fJQn/XT+z/gks9sd64eB+Mf1UQP0d5SHQ8RwbrzIwYFW+FgU/nx8/hJm+nrBAoO9azHOY5aDeoffeLmOFXLD9w==} peerDependencies: prosemirror-model: ^1.7.1 From fd038a671595ef7eb443516c4372de34648bc19f Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 17:33:48 +0800 Subject: [PATCH 10/27] server: remove console --- packages/server/src/services/system.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/src/services/system.service.ts b/packages/server/src/services/system.service.ts index 27b4e06..7031d26 100644 --- a/packages/server/src/services/system.service.ts +++ b/packages/server/src/services/system.service.ts @@ -109,8 +109,6 @@ export class SystemService { ...mail, }, (err, info) => { - console.log('fas', err, info); - if (err) { reject(err); } else { From a80dc769ce77b9476c741371e9a359edf7edd035 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:26:04 +0800 Subject: [PATCH 11/27] server: fix js error --- packages/server/src/services/user.service.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts index d5c459f..e877d34 100644 --- a/packages/server/src/services/user.service.ts +++ b/packages/server/src/services/user.service.ts @@ -115,6 +115,12 @@ export class UserService { * @returns */ async createUser(user: RegisterUserDto): Promise { + const currentSystemConfig = await this.systemService.getConfigFromDatabase(); + + if (currentSystemConfig.isSystemLocked) { + throw new HttpException('系统维护中,暂不可注册', HttpStatus.FORBIDDEN); + } + if (await this.userRepo.findOne({ name: user.name })) { throw new HttpException('该账户已被注册', HttpStatus.BAD_REQUEST); } @@ -157,6 +163,12 @@ export class UserService { * @param registerUser */ public async resetPassword(resetPasswordDto: ResetPasswordDto) { + const currentSystemConfig = await this.systemService.getConfigFromDatabase(); + + if (currentSystemConfig.isSystemLocked) { + throw new HttpException('系统维护中,暂不可使用', HttpStatus.FORBIDDEN); + } + const { email, password, confirmPassword, verifyCode } = resetPasswordDto; const inDatabaseUser = await this.userRepo.findOne({ email }); @@ -195,7 +207,9 @@ export class UserService { existUser = await this.userRepo.findOne({ where: { email: name } }); } - if (!existUser.isSystemAdmin && currentSystemConfig.isSystemLocked) { + const isExistUserSystemAdmin = existUser ? existUser.isSystemAdmin : false; + + if (currentSystemConfig.isSystemLocked && !isExistUserSystemAdmin) { throw new HttpException('系统维护中,暂不可登录', HttpStatus.FORBIDDEN); } From a0c3bf206bafade744028078f2dcb7cc4f374f35 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:26:30 +0800 Subject: [PATCH 12/27] chore: update docker config --- .gitignore | 1 + Dockerfile | 11 +--- config/dev.yaml | 4 +- .../docker-prod-sample.yaml | 25 +++++--- docker-compose.yml | 64 +++++++++++-------- docker/start.sh | 8 +-- nginx.conf.bak => nginx.conf.sample | 0 7 files changed, 66 insertions(+), 47 deletions(-) rename docker/prod-sample.yaml => config/docker-prod-sample.yaml (74%) rename nginx.conf.bak => nginx.conf.sample (100%) diff --git a/.gitignore b/.gitignore index fb92985..44719a1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ tsconfig.tsbuildinfo scripts/update.sh output +runtime diff --git a/Dockerfile b/Dockerfile index 95f5c87..751a6b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,13 @@ FROM node:18-alpine as builder -COPY . /app/ +COPY . /app/ WORKDIR /app -ARG EIP=mrdoc.fun RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories RUN npm config set registry https://registry.npmmirror.com RUN npm i -g pm2 @nestjs/cli pnpm -RUN apk --no-cache add bash -RUN sed -i "s/localhost/$EIP/g" /app/docker/prod-sample.yaml -RUN cp -f /app/docker/prod-sample.yaml /app/config/prod.yaml +RUN apk --no-cache add bash RUN bash build-output.sh - FROM node:18-alpine as prod -LABEL maintainer="www.mrdoc.fun" ENV TZ=Asia/Shanghai COPY --from=builder /app/docker/* /app/docker/ COPY --from=builder /app/output/ /app/ @@ -22,7 +17,7 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositorie RUN npm config set registry https://registry.npmmirror.com RUN set -x \ && apk update \ - && apk add --no-cache tzdata redis \ + && apk add --no-cache tzdata redis \ && chmod +x /app/docker/start.sh \ && npm i -g pm2 @nestjs/cli pnpm \ && rm -rf /var/cache/apk/* diff --git a/config/dev.yaml b/config/dev.yaml index 730569d..52fb7d9 100644 --- a/config/dev.yaml +++ b/config/dev.yaml @@ -37,8 +37,8 @@ server: db: mysql: host: '127.0.0.1' - username: 'root' - password: 'root' + username: 'think' + password: 'think' database: 'think' port: 3306 charset: 'utf8mb4' diff --git a/docker/prod-sample.yaml b/config/docker-prod-sample.yaml similarity index 74% rename from docker/prod-sample.yaml rename to config/docker-prod-sample.yaml index 27cae96..b3198df 100644 --- a/docker/prod-sample.yaml +++ b/config/docker-prod-sample.yaml @@ -1,13 +1,13 @@ -# 生产环境docker示例配置 +# 开发环境配置 client: port: 5001 assetPrefix: '/' apiUrl: 'http://localhost:5002/api' - collaborationUrl: 'ws://localhost:5003/think/wss' + collaborationUrl: 'ws://localhost:5003' # 以下为页面 meta 配置 seoAppName: '云策文档' seoDescription: '云策文档是一款开源知识管理工具。通过独立的知识库空间,结构化地组织在线协作文档,实现知识的积累与沉淀,促进知识的复用与流通。' - seoKeywords: '云策文档,协作,文档,前端面试题,fantasticit,https://github.com/fantasticit/think' + seoKeywords: '云策文档,协作,文档,fantasticit,https://github.com/fantasticit/think' # 预先连接的来源,空格分割(比如图片存储服务器) dnsPrefetch: '//wipi.oss-cn-shanghai.aliyuncs.com' # 站点地址(如:http://think.codingit.cn/),一定要设置,否则会出现 cookie、跨域等问题 @@ -23,22 +23,31 @@ server: enableRateLimit: true # 是否限流 rateLimitWindowMs: 60000 # 限流时间 rateLimitMax: 1000 # 单位限流时间内单个 ip 最大访问数量 + email: # 邮箱服务,参考 http://help.163.com/09/1223/14/5R7P6CJ600753VB8.html?servCode=6010376 获取 SMTP 配置 + host: '' + port: 465 + user: '' + password: '' + admin: + name: 'sytemadmin' # 注意修改 + password: 'sytemadmin' # 注意修改 + email: 'sytemadmin@think.com' # 注意修改为真实邮箱地址 # 数据库配置 db: mysql: - host: 'mysql-with-think' - username: 'jonnyan404' - password: 'www.mrdoc.fun' + host: 'mysql-for-think' + username: 'think' + password: 'think' database: 'think' port: 3306 charset: 'utf8mb4' timezone: '+08:00' synchronize: true redis: - host: '127.0.0.1' + host: 'redis-for-think' port: '6379' - password: '' + password: 'root' # oss 文件存储服务 oss: diff --git a/docker-compose.yml b/docker-compose.yml index c776d79..0e4ea89 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,44 +1,58 @@ -version: "3" +version: '3' services: - thinkdoc: + think: build: context: . - args: - EIP: x.x.x.x # api接口IP,必须设置,可以是 IP 或者域名. image: think - container_name: thinkdoc - #restart: always + container_name: think volumes: - - /path/to/you/dir/config:/app/config # 请注意修改 /path/to/you/dir 为云策文档配置文件目录. - - /path/to/you/dir/static:/app/packages/server/static # 请注意修改 /path/to/you/dir 为云策文档附件存储目录. + - ./config:/app/config + - ./runtime/static:/app/packages/server/static environment: - TZ=Asia/Shanghai ports: - - "5001-5003:5001-5003" + - '5001-5003:5001-5003' depends_on: - mysql + - redis + networks: + - think mysql: image: mysql:5.7 - container_name: mysql-with-think - #restart: always + restart: always + container_name: mysql-for-think volumes: - - /path/to/you/dir/mysql:/var/lib/mysql # 请注意修改 /path/to/you/dir 为您要存储mysql数据的目录绝对路径. + - ./runtime/mysql:/var/lib/mysql environment: - TZ=Asia/Shanghai - - MYSQL_ROOT_PASSWORD=Jonnyan404! + - MYSQL_ROOT_PASSWORD=root - MYSQL_DATABASE=think - - MYSQL_USER=jonnyan404 - - MYSQL_PASSWORD=www.mrdoc.fun + - MYSQL_USER=think + - MYSQL_PASSWORD=think expose: - - "3306" + - '3306' ports: - - "63306:3306" # 如果不需要外部连接mysql,可注释此行+上一行. + - '3306:3306' command: - - "--character-set-server=utf8mb4" - - "--collation-server=utf8mb4_unicode_ci" -# Volumes for persisting data, see https://docs.docker.com/engine/admin/volumes/volumes/ -#volumes: -# thinkdoc-data: -# driver: local -# mysql-data: -# driver: local + - '--character-set-server=utf8mb4' + - '--collation-server=utf8mb4_unicode_ci' + networks: + - think + redis: + image: redis:latest + restart: always + container_name: redis-for-think + command: > + --requirepass root + expose: + - '6379' + ports: + - '6379:6379' + volumes: + - ./runtime/redis:/data + privileged: true + networks: + - think +networks: + think: + driver: bridge diff --git a/docker/start.sh b/docker/start.sh index 60c3e2f..4a488f1 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -1,14 +1,14 @@ #!/bin/sh ### Author:jonnyan404 ### date:2022年5月22日 + CONFIG_FILE='/app/config/prod.yaml' if [ ! -f $CONFIG_FILE ]; then - echo "#####Generating configuration file#####" - cp /app/docker/prod-sample.yaml $CONFIG_FILE + cp -f /app/config/docker-prod-sample.yaml $CONFIG_FILE else - echo "#####Configuration file already exists#####" + echo "" fi -redis-server --daemonize yes + pnpm run pm2 pm2 logs diff --git a/nginx.conf.bak b/nginx.conf.sample similarity index 100% rename from nginx.conf.bak rename to nginx.conf.sample From 896efca88f32cad18dab66049953609442991bf8 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:26:42 +0800 Subject: [PATCH 13/27] chore: update README --- README.md | 175 +----------------------------------------------- let-us-start.md | 110 ++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 173 deletions(-) create mode 100644 let-us-start.md diff --git a/README.md b/README.md index 33e13a4..b3bb0d7 100644 --- a/README.md +++ b/README.md @@ -21,180 +21,15 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 image - ## 预览 ![知识库](http://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-20/YN67GM4VQMBTZFZ88TYP8X/image.png) ![新建文档](http://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-20/YN67GM4VQMBTZFZ88TYPQX/image.png) ![编辑器](http://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-20/YN67GM4VQMBTZFZ88TYPZX/image.png) -## 项目结构 +## 项目开发 -本项目依赖 pnpm 使用 monorepo 形式进行代码组织,分包如下: - -- `@think/config`: 客户端、服务端、OSS、MySQL、Redis 等配置管理 -- `@think/domains`:领域模型数据定义 -- `@think/constants`:常量配置 -- `@think/server`:服务端 -- `@think/client`:客户端 - -## 项目依赖 - -- nodejs ≥ 16.5 -- pnpm -- pm2 -- mysql ≥ 5.7 -- redis (可选) - -依赖安装命令: `npm i -g pm2 @nestjs/cli pnpm` - - - -## Docker-compose 一键构建安装 - -- 实测腾讯轻量云 2C4G 机器构建需 8 分钟左右 - -**请注意构建前修改 `docker-compose.yml` 中的 `EIP` 参数,否则无法正常使用!!!** - -``` -# 首次安装 -git clone https://github.com/fantasticit/think.git -cd think -vim docker-compose.yml -docker-compose up -d - -# 二次更新升级 -cd think -git pull -docker-compose build -docker-compose up -d - -# FAQ -如遇二次更新有问题,请更新代码重新构建,然后删除本地配置文件并重启容器. -如果还不能解决,1.有能力可自行解决|2.等待更新|3.去mrdoc.fun站点留言 -``` - -然后访问 `http://ip:5001` 即可. - - - -## 手动安装教程 - -- 前台页面地址:`http://localhost:5001` -- 服务接口地址:`http://localhost:5002` -- 协作接口地址:`http://localhost:5003` - -如需修改配置,开发环境编辑 `config/dev.yaml`。生产环境编辑 `config/prod.yaml` (如没有,可复制开发环境的配置修改即可.) - -- 数据库 - -首先安装 `MySQL`,推荐使用 docker 进行安装。 - -```bash -docker image pull mysql:5.7 -# m1 的 mac 可以用:docker image pull --platform linux/x86_64 mysql:5.7 -docker run -d --restart=always --name think -p 3306:3306 -e MYSQL_DATABASE=think -e MYSQL_ROOT_PASSWORD=root mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci -``` - -- 可选:Redis - -如果需要文档版本服务,请在配置文件中修改 `db.redis` 的配置。 - -``` -docker pull redis:latest -docker run --name think-redis -p 6379:6379 -d redis --appendonly yes --requirepass "root" -``` - - -### 本地源代码运行(开发环境) - - -```bash -git clone https://github.com/fantasticit/think.git -cd think -pnpm install -pnpm run dev -``` - -然后访问 `http://ip:5001` 即可. - - - -### 本地源代码运行(生产环境) - -生产环境部署的脚本如下: - -```bash -git clone https://github.com/fantasticit/think.git -cd think -pnpm install -pnpm run build -pnpm run pm2 - -pm2 startup -pm2 save -``` - -### nginx 配置参考 - -采用 `nginx` 作为反向代理的配置参考(部分),完整版请见 <[think/nginx.conf.bak](https://github.com/fantasticit/think/blob/main/nginx.conf.bak)> - -```bash -upstream wipi_client { - server 127.0.0.1:5001; - keepalive 64; -} - -# http -> https 重定向 -server { - listen 80; - server_name 域名; - rewrite ^(.*)$ https://$host$1 permanent; -} - -server { - listen 443 ssl; - server_name 域名; - ssl_certificate 证书存放路径; - ssl_certificate_key 证书存放路径; - - location / { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Nginx-Proxy true; - proxy_cache_bypass $http_upgrade; - proxy_pass http://wipi_client; #反向代理 - proxy_set_header X-Real-IP $remote_addr; #获取客户端真实IP - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } -} -``` - -### caddy2 配置参考 - -采用 caddy v2 作为反向代理的配置文件参考 - -``` -# 例子中的域名,请自行替换. -think.mrdoc.fun { - encode zstd gzip - reverse_proxy localhost:5001 -} - - -thinkapi.mrdoc.fun { - @websockets { - header Connection *Upgrade* - header Upgrade websocket - path /think/wss/* -} - encode zstd gzip - reverse_proxy /api/* localhost:5002 - reverse_proxy @websockets localhost:5003 -} -``` +[项目开发说明](./let-us-start.md)。 ## 自动化部署 @@ -202,21 +37,15 @@ thinkapi.mrdoc.fun { 参考:[webhook](https://github.com/adnanh/webhook/blob/master/docs/Hook-Examples.md#incoming-github-webhook) -## 商用 - -如需商用,请联系作者,取得授权后可商用。 - ## 赞助 如果这个项目对您有帮助,并且您希望支持该项目的开发和维护,请随时扫描一下二维码进行捐赠。非常感谢您的捐款,谢谢! -如果您希望留下您的信息,可以到[感谢信](https://think.codingit.cn/wiki/eb520cdf-aa4b-4af2-ae4a-7140e21403ab/document/230548f5-3220-4c5b-a209-02b1eb0299e7)评论区留言。
alipay wechat
- ## 资料 - next.js 源码:https://github.com/vercel/next.js diff --git a/let-us-start.md b/let-us-start.md new file mode 100644 index 0000000..17bd5f7 --- /dev/null +++ b/let-us-start.md @@ -0,0 +1,110 @@ +# think + +## 项目结构 + +本项目依赖 pnpm 使用 monorepo 形式进行代码组织,分包如下: + +- `@think/config`: 客户端、服务端、OSS、MySQL、Redis 等配置管理 +- `@think/domains`:领域模型数据定义 +- `@think/constants`:常量配置 +- `@think/server`:服务端 +- `@think/client`:客户端 + +## 项目依赖 + +为了将项目运行起来,至少需要以下依赖。 + +- nodejs >=16.5.0:推荐使用 nvm 安装 +- pnpm:安装 nodejs 后,运行 `npm i -g pnpm` 即可安装 +- pm2:安装 nodejs 后,运行 `npm i -g pm2` 即可安装 +- MySQL 5.7 +- Redis + +## 配置文件 + +项目所有的配置文件都在 `config` 目录下,其中 `dev.yaml` 中各字段均有解释,生产环境打包依赖 `prod.yaml`(需要自行修改为所需配置)。如果运行不起来,请对比 `dev.yaml` 检查配置。 + +**如果部署遇到问题,首先请确认相应配置是否正确!** + +## 项目运行 + +无论是开发环境,还是生产环境,项目运行成功后会在 3 个端口启动相应服务(默认 5001、5002、5003),具体端口号由 `config` 文件夹下的配置文件决定。 + +- 前台页面地址:`http://localhost:5001` +- 服务接口地址:`http://localhost:5002` +- 协作接口地址:`http://localhost:5003` + +### 本地开发 + +1. 安装数据库 + +首先安装 `MySQL` 和 `Redis`,推荐使用 docker 进行安装。 + +```bash +docker image pull mysql:5.7 +# m1 的 mac 可以用:docker image pull --platform linux/x86_64 mysql:5.7 +docker run -d --restart=always --name mysql-for-think-dev -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_USER=think -e MYSQL_PASSWORD=think -e MYSQL_DATABASE=think mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + +docker pull redis:latest +docker run --name redis-for-think-dev -p 6379:6379 -d redis --appendonly yes --requirepass "root" +``` + +2. 安装依赖并运行 + +```bash +git clone https://github.com/fantasticit/think.git +cd think +pnpm install +pnpm run dev +``` + +### 生产部署 + +首先确认在 `config` 文件夹下新建 `prod.yaml` 配置文件,然后运行以下命令。 + +```bash +git clone https://github.com/fantasticit/think.git +cd think +pnpm install # 安装依赖 +pnpm run build # 项目打包 + +# 以下如果没有安装 pm2,直接 pnpm run start,推荐使用 pm2 +pnpm run pm2 +pm2 startup +pm2 save +``` + +### docker-compose + +也可以使用 docker-compose 进行项目部署。首先,根据需要修改 `docker-compose.yml` 中的数据库、Redis 相关用户名、密码等配置,然后,修改 `config/docker-prod-sample.yaml` 中对应的配置。 + +```bash +# 首次安装 +git clone https://github.com/fantasticit/think.git +cd think +vim docker-compose.yml +docker-compose up -d + +# 二次更新升级 +cd think +git pull +docker-compose build +docker-compose up -d + +# 如果二次更新有问题 +docker-compose kill +docker-compose rm +docker image rm think # 删掉构建的镜像 +docker-compose up -d +``` + +### nginx 配置参考 + +无论以何种方式进行项目部署,项目运行成功后会在 3 个端口启动服务(默认 5001、5002、5003,具体由配置文件决定)。`nginx` 配置参考 <[think/nginx.conf.sample](https://github.com/fantasticit/think/blob/main/nginx.conf.sample)>。 + +特别强调,在 `config` 文件夹的配置中 `client.siteUrl` 一定要配置正确,否则客户端可能无法正常运行。 + +```yaml +# 站点地址(如:http://think.codingit.cn/),一定要设置,否则会出现 cookie、跨域等问题 +siteUrl: 'http://localhost:5001' +``` From b4af8a5ebcd6a6ee52e9a91738489c0b74e49b25 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:36:16 +0800 Subject: [PATCH 14/27] chore: update README --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b3bb0d7..3a72f20 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,8 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 wechat -## 资料 +## 贡献者 -- next.js 源码:https://github.com/vercel/next.js -- next.js 文档:https://nextjs.org/ -- nest.js 源码:https://github.com/nestjs/nest -- nest.js 文档:https://nestjs.com/ +感谢所有为本项目作出贡献的同学! + + From 27778d0bde701b6b8156ff1c99ba567815d8a4a9 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:36:58 +0800 Subject: [PATCH 15/27] chore: update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a72f20..b90940b 100644 --- a/README.md +++ b/README.md @@ -50,4 +50,4 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 感谢所有为本项目作出贡献的同学! - + From 23a62b06a16a776f7b9d3cf983d2ca55d65ae8e4 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:41:27 +0800 Subject: [PATCH 16/27] chore: update README --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b90940b..7926d2a 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,12 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 ## 预览 -![知识库](http://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-20/YN67GM4VQMBTZFZ88TYP8X/image.png) -![新建文档](http://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-20/YN67GM4VQMBTZFZ88TYPQX/image.png) -![编辑器](http://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-20/YN67GM4VQMBTZFZ88TYPZX/image.png) +
+ 查看预览图 + 知识库 + 新建文档 + 编辑器 +
## 项目开发 From 9bf8112c9ede9b47dcc186164e4f12cb599199de Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 20:43:09 +0800 Subject: [PATCH 17/27] chore: update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7926d2a..f7a6778 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 欢迎进群交流。 -image +image ## 预览 From 24f7e9ae61e68c0f08b4cb0b57e8ee24e6819a86 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 21:12:33 +0800 Subject: [PATCH 18/27] server: fix delete verify record --- packages/server/src/services/verify.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/services/verify.service.ts b/packages/server/src/services/verify.service.ts index cb3e5f9..bf1371c 100644 --- a/packages/server/src/services/verify.service.ts +++ b/packages/server/src/services/verify.service.ts @@ -21,7 +21,8 @@ export class VerifyService { * @param record */ private async deleteVerifyCode(id) { - await this.verifyRepo.remove(await this.verifyRepo.find(id)); + const record = await this.verifyRepo.findOne(id); + await this.verifyRepo.remove(record); } /** From d7a9f68af478cba73d5415620d719af45562b16d Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 21:16:53 +0800 Subject: [PATCH 19/27] client: fix seo title --- packages/client/src/pages/admin/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/pages/admin/index.tsx b/packages/client/src/pages/admin/index.tsx index 78062d3..93d9851 100644 --- a/packages/client/src/pages/admin/index.tsx +++ b/packages/client/src/pages/admin/index.tsx @@ -28,7 +28,7 @@ const Page: NextPage = () => { return ( - +
{user && user.isSystemAdmin ? ( <> From fccfaa09b7a92f1f8f579c8326b57e71810c6da7 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 21:37:39 +0800 Subject: [PATCH 20/27] chore: update nginx.conf --- nginx.conf.sample | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index fca91b5..f2b36de 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -43,16 +43,23 @@ server { } location /think/wss { - proxy_pass http://think_wss; - proxy_read_timeout 300s; - proxy_send_timeout 300s; + proxy_pass http://think_wss; + proxy_read_timeout 300s; + proxy_send_timeout 300s; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + } + + location /static/ { + gzip_static on; + expires max; + add_header Cache-Control public; + alias /apps/think/packages/server/static/; } } From bf4389624bfba0565dbd0901589c072fc68f19e3 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 21:16:53 +0800 Subject: [PATCH 21/27] client: fix seo title --- packages/client/src/pages/admin/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/pages/admin/index.tsx b/packages/client/src/pages/admin/index.tsx index 78062d3..93d9851 100644 --- a/packages/client/src/pages/admin/index.tsx +++ b/packages/client/src/pages/admin/index.tsx @@ -28,7 +28,7 @@ const Page: NextPage = () => { return ( - +
{user && user.isSystemAdmin ? ( <> From d7fbbb1700e6360ddfce25e1290c32ad8f7aaa4f Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 21:37:39 +0800 Subject: [PATCH 22/27] chore: update nginx.conf --- nginx.conf.sample | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index fca91b5..f2b36de 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -43,16 +43,23 @@ server { } location /think/wss { - proxy_pass http://think_wss; - proxy_read_timeout 300s; - proxy_send_timeout 300s; + proxy_pass http://think_wss; + proxy_read_timeout 300s; + proxy_send_timeout 300s; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + } + + location /static/ { + gzip_static on; + expires max; + add_header Cache-Control public; + alias /apps/think/packages/server/static/; } } From bf0826b2205f1aacf8770b79af209833f25493f6 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Tue, 28 Jun 2022 23:46:57 +0800 Subject: [PATCH 23/27] chore: update docker config --- Dockerfile | 2 +- build-output.sh | 7 ++++++- config/dev.yaml | 6 +++--- config/docker-prod-sample.yaml | 6 +++--- docker/start.sh | 10 ++-------- let-us-start.md | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 751a6b8..6ad5a10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ COPY --from=builder /app/output/ /app/ WORKDIR /app RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories RUN npm config set registry https://registry.npmmirror.com -RUN set -x \ +RUN set -x \ && apk update \ && apk add --no-cache tzdata redis \ && chmod +x /app/docker/start.sh \ diff --git a/build-output.sh b/build-output.sh index a2caa46..ed23432 100755 --- a/build-output.sh +++ b/build-output.sh @@ -1,7 +1,12 @@ #! /bin/bash # 该脚本只保留生产环境运行所需文件到统一目录 +if [ ! -f './config/prod.yaml' ]; then + echo "缺少 config/prod.yaml 文件,可参考 docker-prod-sample.yaml 进行配置" + exit 1 +fi # 构建 +pnpm fetch --prod pnpm install pnpm run build @@ -71,7 +76,7 @@ cd ../../ # @see https://github.com/typicode/husky/issues/914#issuecomment-826768549 cd ${outputDir} npm set-script prepare "" -pnpm install -r --prod +pnpm install -r --offline --prod cd ../ echo "${outputDir} 打包完成" diff --git a/config/dev.yaml b/config/dev.yaml index 52fb7d9..1b7e340 100644 --- a/config/dev.yaml +++ b/config/dev.yaml @@ -29,9 +29,9 @@ server: user: '' password: '' admin: - name: 'sytemadmin' # 注意修改 - password: 'sytemadmin' # 注意修改 - email: 'sytemadmin@think.com' # 注意修改为真实邮箱地址 + name: 'admin' # 注意修改 + password: 'admin' # 注意修改 + email: 'admin@think.com' # 注意修改为真实邮箱地址 # 数据库配置 db: diff --git a/config/docker-prod-sample.yaml b/config/docker-prod-sample.yaml index b3198df..103f5c1 100644 --- a/config/docker-prod-sample.yaml +++ b/config/docker-prod-sample.yaml @@ -29,9 +29,9 @@ server: user: '' password: '' admin: - name: 'sytemadmin' # 注意修改 - password: 'sytemadmin' # 注意修改 - email: 'sytemadmin@think.com' # 注意修改为真实邮箱地址 + name: 'admin' # 注意修改 + password: 'admin' # 注意修改 + email: 'admin@think.com' # 注意修改为真实邮箱地址 # 数据库配置 db: diff --git a/docker/start.sh b/docker/start.sh index 4a488f1..821e6cc 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -2,13 +2,7 @@ ### Author:jonnyan404 ### date:2022年5月22日 -CONFIG_FILE='/app/config/prod.yaml' - -if [ ! -f $CONFIG_FILE ]; then - cp -f /app/config/docker-prod-sample.yaml $CONFIG_FILE -else - echo "" -fi - pnpm run pm2 +pm2 startup +pm2 save pm2 logs diff --git a/let-us-start.md b/let-us-start.md index 17bd5f7..1fbfbea 100644 --- a/let-us-start.md +++ b/let-us-start.md @@ -76,7 +76,7 @@ pm2 save ### docker-compose -也可以使用 docker-compose 进行项目部署。首先,根据需要修改 `docker-compose.yml` 中的数据库、Redis 相关用户名、密码等配置,然后,修改 `config/docker-prod-sample.yaml` 中对应的配置。 +也可以使用 docker-compose 进行项目部署。首先,根据需要修改 `docker-compose.yml` 中的数据库、Redis 相关用户名、密码等配置,然后,从 `config/docker-prod-sample.yaml` 复制出 `config/prod.yaml` 并修改其中对应的配置。 ```bash # 首次安装 From fb6fdfffb50474aebc9a9037d07d9b8001bf8265 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Wed, 29 Jun 2022 00:04:22 +0800 Subject: [PATCH 24/27] feat: remove confirmPassword --- packages/client/src/helpers/validator.ts | 7 +++++++ .../src/pages/register/index.module.scss | 1 + packages/client/src/pages/register/index.tsx | 21 ++++++++++--------- packages/server/src/dtos/create-user.dto.ts | 7 +------ packages/server/src/services/user.service.ts | 4 ---- 5 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 packages/client/src/helpers/validator.ts diff --git a/packages/client/src/helpers/validator.ts b/packages/client/src/helpers/validator.ts new file mode 100644 index 0000000..126e236 --- /dev/null +++ b/packages/client/src/helpers/validator.ts @@ -0,0 +1,7 @@ +export const isEmail = (email) => { + return !!String(email) + .toLowerCase() + .match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + ); +}; diff --git a/packages/client/src/pages/register/index.module.scss b/packages/client/src/pages/register/index.module.scss index 3b94d33..4366dc2 100644 --- a/packages/client/src/pages/register/index.module.scss +++ b/packages/client/src/pages/register/index.module.scss @@ -9,6 +9,7 @@ position: relative; z-index: 10; display: flex; + height: calc(100% - 52px); padding: 10vh 24px; flex: 1; flex-direction: column; diff --git a/packages/client/src/pages/register/index.tsx b/packages/client/src/pages/register/index.tsx index cb6329f..e414e52 100644 --- a/packages/client/src/pages/register/index.tsx +++ b/packages/client/src/pages/register/index.tsx @@ -3,11 +3,13 @@ import { Author } from 'components/author'; import { LogoImage, LogoText } from 'components/logo'; import { Seo } from 'components/seo'; import { useRegister, useVerifyCode } from 'data/user'; +import { isEmail } from 'helpers/validator'; import { useInterval } from 'hooks/use-interval'; import { useRouterQuery } from 'hooks/use-router-query'; import { useToggle } from 'hooks/use-toggle'; import Link from 'next/link'; import Router from 'next/router'; +import { emit } from 'process'; import React, { useCallback, useState } from 'react'; import styles from './index.module.scss'; @@ -25,7 +27,13 @@ const Page = () => { const { sendVerifyCode, loading: sendVerifyCodeLoading } = useVerifyCode(); const onFormChange = useCallback((formState) => { - setEmail(formState.values.email); + const email = formState.values.email; + + if (isEmail(email)) { + setEmail(email); + } else { + setEmail(null); + } }, []); const { start, stop } = useInterval(() => { @@ -89,6 +97,7 @@ const Page = () => { 用户注册 + { placeholder="输入账户名称" rules={[{ required: true, message: '请输入账户' }]} > + { placeholder="输入用户密码" rules={[{ required: true, message: '请输入密码' }]} > - Date: Wed, 29 Jun 2022 00:20:12 +0800 Subject: [PATCH 25/27] server: use shortId --- .../server/src/entities/document.entity.ts | 10 ++++- packages/server/src/entities/wiki.entity.ts | 10 ++++- .../server/src/helpers/shortid.herlper.ts | 43 +++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 packages/server/src/helpers/shortid.herlper.ts diff --git a/packages/server/src/entities/document.entity.ts b/packages/server/src/entities/document.entity.ts index 7783e3a..b1ee38a 100644 --- a/packages/server/src/entities/document.entity.ts +++ b/packages/server/src/entities/document.entity.ts @@ -1,10 +1,16 @@ +import { getShortId } from '@helpers/shortid.herlper'; import { DocumentStatus } from '@think/domains'; import { Exclude } from 'class-transformer'; -import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import { BeforeInsert, Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'; @Entity('document') export class DocumentEntity { - @PrimaryGeneratedColumn('uuid') + @BeforeInsert() + getShortId() { + this.id = getShortId(); + } + + @PrimaryColumn() public id: string; @Column({ type: 'varchar', comment: '文档所属知识库 Id' }) diff --git a/packages/server/src/entities/wiki.entity.ts b/packages/server/src/entities/wiki.entity.ts index c3861cb..988b1b3 100644 --- a/packages/server/src/entities/wiki.entity.ts +++ b/packages/server/src/entities/wiki.entity.ts @@ -1,10 +1,16 @@ +import { getShortId } from '@helpers/shortid.herlper'; import { DEFAULT_WIKI_AVATAR } from '@think/constants'; import { WikiStatus } from '@think/domains'; -import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import { BeforeInsert, Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'; @Entity('wiki') export class WikiEntity { - @PrimaryGeneratedColumn('uuid') + @BeforeInsert() + getShortId() { + this.id = getShortId(); + } + + @PrimaryColumn() public id: string; @Column({ type: 'varchar', length: 200, comment: '知识库名称' }) diff --git a/packages/server/src/helpers/shortid.herlper.ts b/packages/server/src/helpers/shortid.herlper.ts new file mode 100644 index 0000000..043fc0e --- /dev/null +++ b/packages/server/src/helpers/shortid.herlper.ts @@ -0,0 +1,43 @@ +import { randomFillSync } from 'node:crypto'; + +const urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'; + +// It is best to make fewer, larger requests to the crypto module to +// avoid system call overhead. So, random numbers are generated in a +// pool. The pool is a Buffer that is larger than the initial random +// request size by this multiplier. The pool is enlarged if subsequent +// requests exceed the maximum buffer size. +const POOL_SIZE_MULTIPLIER = 128; +let pool, poolOffset; + +const fillPool = (bytes) => { + if (!pool || pool.length < bytes) { + pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER); + randomFillSync(pool); + poolOffset = 0; + } else if (poolOffset + bytes > pool.length) { + randomFillSync(pool); + poolOffset = 0; + } + poolOffset += bytes; +}; + +const nanoid = (size = 21) => { + // `-=` convert `size` to number to prevent `valueOf` abusing + fillPool((size -= 0)); + let id = ''; + // We are reading directly from the random pool to avoid creating new array + for (let i = poolOffset - size; i < poolOffset; i++) { + // It is incorrect to use bytes exceeding the alphabet size. + // The following mask reduces the random byte in the 0-255 value + // range to the 0-63 value range. Therefore, adding hacks, such + // as empty string fallback or magic numbers, is unneccessary because + // the bitmask trims bytes down to the alphabet size. + id += urlAlphabet[pool[i] & 63]; + } + return id; +}; + +export const getShortId = () => { + return nanoid(12); +}; From 1b4b33458b6219671b66fbd6f70690b82ef24c02 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Wed, 29 Jun 2022 00:56:58 +0800 Subject: [PATCH 26/27] server: use shortId --- packages/server/src/entities/template.entity.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/server/src/entities/template.entity.ts b/packages/server/src/entities/template.entity.ts index 77ee93e..9ac379e 100644 --- a/packages/server/src/entities/template.entity.ts +++ b/packages/server/src/entities/template.entity.ts @@ -1,8 +1,14 @@ -import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import { getShortId } from '@helpers/shortid.herlper'; +import { BeforeInsert, Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'; @Entity('template') export class TemplateEntity { - @PrimaryGeneratedColumn('uuid') + @BeforeInsert() + getShortId() { + this.id = getShortId(); + } + + @PrimaryColumn() public id: string; @Column({ type: 'boolean', default: false, comment: '是否公开' }) From a79384df406fe234f650e91cfa1df37466e47d14 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Wed, 29 Jun 2022 01:11:01 +0800 Subject: [PATCH 27/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7a6778..fa4bbdd 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Think 是一款开源知识管理工具。通过独立的知识库空间,结 - `nest.js`:服务端框架 - `tiptap`:编辑器及文档协作 -可访问[云策文档帮助中心](https://think.codingit.cn/share/wiki/eb520cdf-aa4b-4af2-ae4a-7140e21403ab),查看更多功能文档。 +可访问[云策文档帮助中心](https://think.codingit.cn/share/wiki/JtXHW2BjrQ6G),查看更多功能文档。 ## 链接