From 02e92def52fb13415a82baf384cf47ef5713d0bb Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:14:33 +0900 Subject: [PATCH 01/20] Disable retry logic for 401 errors during API call (#325) * Disable retrying fetching user when token is expired * Disable retrying fetching when the error status code is 401 --- frontend/src/App.tsx | 9 +++++++++ frontend/src/hooks/api/user.ts | 5 +---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c8e6c966..9e99e260 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -88,6 +88,15 @@ function App() { }, }), defaultOptions: { + queries: { + retry: (failureCount, error) => { + if (axios.isAxiosError(error) && error.response?.status === 401) { + return false; + } + + return failureCount < 3; + }, + }, mutations: { onError: handleError, }, diff --git a/frontend/src/hooks/api/user.ts b/frontend/src/hooks/api/user.ts index f4b99026..cf673868 100644 --- a/frontend/src/hooks/api/user.ts +++ b/frontend/src/hooks/api/user.ts @@ -14,14 +14,11 @@ export const useGetUserQuery = () => { const dispatch = useDispatch(); const authStore = useSelector(selectAuth); - if (authStore.accessToken) { - axios.defaults.headers.common["Authorization"] = `Bearer ${authStore.accessToken}`; - } - const query = useQuery({ queryKey: generateGetUserQueryKey(authStore.accessToken || ""), enabled: Boolean(authStore.accessToken), queryFn: async () => { + axios.defaults.headers.common["Authorization"] = `Bearer ${authStore.accessToken}`; const res = await axios.get("/users"); return res.data; }, From ec76b55fc31debc6018795e9e5dc198aed56bb21 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:32:25 +0900 Subject: [PATCH 02/20] Downgrade `yorkie-js-sdk` to `v0.4.27` (#327) --- backend/docker/docker-compose-full.yml | 2 +- backend/docker/docker-compose.yml | 2 +- frontend/package-lock.json | 542 ++++++++++++++++--------- frontend/package.json | 2 +- 4 files changed, 356 insertions(+), 192 deletions(-) diff --git a/backend/docker/docker-compose-full.yml b/backend/docker/docker-compose-full.yml index a42dc14e..de021427 100644 --- a/backend/docker/docker-compose-full.yml +++ b/backend/docker/docker-compose-full.yml @@ -34,7 +34,7 @@ services: - "yorkie:yorkie" yorkie: - image: "yorkieteam/yorkie:0.4.31" + image: "yorkieteam/yorkie:0.4.27" command: ["server", "--enable-pprof"] restart: always ports: diff --git a/backend/docker/docker-compose.yml b/backend/docker/docker-compose.yml index 3693c2fa..cc3521dc 100644 --- a/backend/docker/docker-compose.yml +++ b/backend/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: yorkie: - image: "yorkieteam/yorkie:0.4.31" + image: "yorkieteam/yorkie:0.4.27" command: ["server", "--enable-pprof"] restart: always ports: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5d1ebae8..7f83b5b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -61,7 +61,7 @@ "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "^0.4.31" + "yorkie-js-sdk": "^0.4.27" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", @@ -167,12 +167,12 @@ "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.25.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", - "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -283,14 +283,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -312,12 +312,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -359,9 +359,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -385,16 +385,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -403,9 +403,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -441,9 +441,9 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz", - "integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.1.tgz", + "integrity": "sha512-iBfKbyIoXS1FGdsKcZmnrxmbc8VcbMrSgD7AVrsnX+WyAYjmUDWvE93dt5D874qS4CCVu4O1JpbagHdXbbLiOw==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -555,9 +555,9 @@ "license": "MIT" }, "node_modules/@codemirror/view": { - "version": "6.32.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.32.0.tgz", - "integrity": "sha512-AgVNvED2QTsZp5e3syoHLsrWtwJFYWdx1Vr/m3f4h1ATQz0ax60CfXF3Htdmk69k2MlYZw8gXesnQdHtzyVmAw==", + "version": "6.33.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.33.0.tgz", + "integrity": "sha512-AroaR3BvnjRW8fiZBalAaK+ZzB5usGgI014YKElYZvQdNH5ZIidHlO+cyf/2rWzyBFRkvG6VhiXeAEbC53P2YQ==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.4.0", @@ -1462,7 +1462,7 @@ } } }, - "node_modules/@mui/private-theming": { + "node_modules/@mui/material/node_modules/@mui/private-theming": { "version": "5.16.6", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", @@ -1489,7 +1489,7 @@ } } }, - "node_modules/@mui/styled-engine": { + "node_modules/@mui/material/node_modules/@mui/styled-engine": { "version": "5.16.6", "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", @@ -1521,7 +1521,7 @@ } } }, - "node_modules/@mui/system": { + "node_modules/@mui/material/node_modules/@mui/system": { "version": "5.16.7", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", @@ -1561,13 +1561,177 @@ } } }, + "node_modules/@mui/private-theming": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.0.1.tgz", + "integrity": "sha512-jQCJml1OwIrhqN5tTk5Lpqx2RZKQnShE8lMlvAkuO7Ft+xaHkP8J3iHpEk3/Pzue34DfBQtK00jcaplgM47mBA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "@mui/utils": "^6.0.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming/node_modules/@mui/utils": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.1.tgz", + "integrity": "sha512-YmQYb2tY5nJactHltTrKA15TZfbd1R003a2xYHxUuycTv9n83rsIwHkypOxM4x7+c+Pc8xfCuE9EfLT3B3n40Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "@mui/types": "^7.2.16", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.0.1.tgz", + "integrity": "sha512-7ZOnUhIak2vosDgMlBE/oLrsvvF3O8QKmTFpP6bhZkHjPu4dv0DbF1vC7gzgkOqiMaT0/NgRQCFW9zh38pIvsg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "@emotion/cache": "^11.13.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.0.1.tgz", + "integrity": "sha512-RdWyCMi+GkAekOnpMKhy51lyzid4F6Vj96vekp3AExkFY21JWg2+KVBqcAgJOROJ3RiaeDJf98n0yrixlCvuEw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "@mui/private-theming": "^6.0.1", + "@mui/styled-engine": "^6.0.1", + "@mui/types": "^7.2.16", + "@mui/utils": "^6.0.1", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/@mui/utils": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.1.tgz", + "integrity": "sha512-YmQYb2tY5nJactHltTrKA15TZfbd1R003a2xYHxUuycTv9n83rsIwHkypOxM4x7+c+Pc8xfCuE9EfLT3B3n40Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "@mui/types": "^7.2.16", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/types": { - "version": "7.2.15", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", - "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", + "version": "7.2.16", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz", + "integrity": "sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==", "license": "MIT", "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1606,13 +1770,12 @@ } }, "node_modules/@mui/x-date-pickers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.14.0.tgz", - "integrity": "sha512-3xI3xYVxqPU4//KfE4FcR+Zs7UT4kkDPvA+IDOcQdRUyVwmcXCjBuJZgKgJMqSCNK/KIJZQQrpmy5XGHOKTbdA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.15.0.tgz", + "integrity": "sha512-YQEQICNxUEFYp/I/yP58cqihA8yhXaXSNZ1/N0JANu2IlCwoJ4Jzi+S0s4RN7RghpiDyoSMFijROBC5HfpTjiw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.25.0", - "@mui/system": "^5.16.7", + "@babel/runtime": "^7.25.4", "@mui/utils": "^5.16.6", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", @@ -1629,7 +1792,8 @@ "peerDependencies": { "@emotion/react": "^11.9.0", "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", "date-fns": "^2.25.0 || ^3.2.0", "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0", "dayjs": "^1.10.7", @@ -1808,9 +1972,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", + "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==", "cpu": [ "arm" ], @@ -1821,9 +1985,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz", + "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==", "cpu": [ "arm64" ], @@ -1834,9 +1998,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz", + "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==", "cpu": [ "arm64" ], @@ -1847,9 +2011,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz", + "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==", "cpu": [ "x64" ], @@ -1860,9 +2024,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz", + "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==", "cpu": [ "arm" ], @@ -1873,9 +2037,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz", + "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==", "cpu": [ "arm" ], @@ -1886,9 +2050,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz", + "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==", "cpu": [ "arm64" ], @@ -1899,9 +2063,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz", + "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==", "cpu": [ "arm64" ], @@ -1912,9 +2076,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz", + "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==", "cpu": [ "ppc64" ], @@ -1925,9 +2089,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz", + "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==", "cpu": [ "riscv64" ], @@ -1938,9 +2102,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz", + "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==", "cpu": [ "s390x" ], @@ -1951,9 +2115,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", + "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", "cpu": [ "x64" ], @@ -1964,9 +2128,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz", + "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==", "cpu": [ "x64" ], @@ -1977,9 +2141,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz", + "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==", "cpu": [ "arm64" ], @@ -1990,9 +2154,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz", + "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==", "cpu": [ "ia32" ], @@ -2003,9 +2167,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz", + "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==", "cpu": [ "x64" ], @@ -2059,9 +2223,9 @@ } }, "node_modules/@sentry/babel-plugin-component-annotate": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.2.tgz", - "integrity": "sha512-6kFAHGcs0npIC4HTt4ULs8uOfEucvMI7VW4hoyk17jhRaW8CbxzxfWCfIeRbDkE8pYwnARaq83tu025Hrk2zgA==", + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.3.tgz", + "integrity": "sha512-OlHA+i+vnQHRIdry4glpiS/xTOtgjmpXOt6IBOUqynx5Jd/iK1+fj+t8CckqOx9wRacO/hru2wfW/jFq0iViLg==", "dev": true, "license": "MIT", "engines": { @@ -2088,14 +2252,14 @@ } }, "node_modules/@sentry/bundler-plugin-core": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.2.tgz", - "integrity": "sha512-TwEEW4FeEJ5Mamp4fGnktfVjzN77KAW0xFQsEPuxZtOAPG17zX/PGvdyRX/TE1jkZWhTzqUDIdgzqlNLjyEnUw==", + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.3.tgz", + "integrity": "sha512-DeoUl0WffcqZZRl5Wy9aHvX4WfZbbWt0QbJ7NJrcEViq+dRAI2FQTYECFLwdZi5Gtb3oyqZICO+P7k8wDnzsjQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.18.5", - "@sentry/babel-plugin-component-annotate": "2.22.2", + "@sentry/babel-plugin-component-annotate": "2.22.3", "@sentry/cli": "^2.33.1", "dotenv": "^16.3.1", "find-up": "^5.0.0", @@ -2108,9 +2272,9 @@ } }, "node_modules/@sentry/cli": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.33.1.tgz", - "integrity": "sha512-dUlZ4EFh98VFRPJ+f6OW3JEYQ7VvqGNMa0AMcmvk07ePNeK/GicAWmSQE4ZfJTTl80ul6HZw1kY01fGQOQlVRA==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.34.1.tgz", + "integrity": "sha512-hAHvu+XH1kn1ee2NUWvuqAZenK/MrxqQzeIrIYATqF2XGjtSOr7irjAKWjd97/vXdLHA6TBnMW1wHwLcuJK2tg==", "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", @@ -2128,19 +2292,19 @@ "node": ">= 10" }, "optionalDependencies": { - "@sentry/cli-darwin": "2.33.1", - "@sentry/cli-linux-arm": "2.33.1", - "@sentry/cli-linux-arm64": "2.33.1", - "@sentry/cli-linux-i686": "2.33.1", - "@sentry/cli-linux-x64": "2.33.1", - "@sentry/cli-win32-i686": "2.33.1", - "@sentry/cli-win32-x64": "2.33.1" + "@sentry/cli-darwin": "2.34.1", + "@sentry/cli-linux-arm": "2.34.1", + "@sentry/cli-linux-arm64": "2.34.1", + "@sentry/cli-linux-i686": "2.34.1", + "@sentry/cli-linux-x64": "2.34.1", + "@sentry/cli-win32-i686": "2.34.1", + "@sentry/cli-win32-x64": "2.34.1" } }, "node_modules/@sentry/cli-darwin": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.33.1.tgz", - "integrity": "sha512-+4/VIx/E1L2hChj5nGf5MHyEPHUNHJ/HoG5RY+B+vyEutGily1c1+DM2bum7RbD0xs6wKLIyup5F02guzSzG8A==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.34.1.tgz", + "integrity": "sha512-SqlCunwhweMDJNKVf3kabiN6FwpvCIffn2cjfaZD0zqZQ3M1tWMJ/kSA0TGfe7lWu9JloNmVm+ArcudGitvX3w==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -2152,9 +2316,9 @@ } }, "node_modules/@sentry/cli-linux-arm": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.33.1.tgz", - "integrity": "sha512-zbxEvQju+tgNvzTOt635le4kS/Fbm2XC2RtYbCTs034Vb8xjrAxLnK0z1bQnStUV8BkeBHtsNVrG+NSQDym2wg==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.34.1.tgz", + "integrity": "sha512-CDhtFbUs16CoU10wEbxnn/pEuenFIMosTcxI7v0gWp3Wo0B2h0bOsLEk9dlT0YsqRTAldKUzef9AVX82m5Svwg==", "cpu": [ "arm" ], @@ -2170,9 +2334,9 @@ } }, "node_modules/@sentry/cli-linux-arm64": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.33.1.tgz", - "integrity": "sha512-DbGV56PRKOLsAZJX27Jt2uZ11QfQEMmWB4cIvxkKcFVE+LJP4MVA+MGGRUL6p+Bs1R9ZUuGbpKGtj0JiG6CoXw==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.34.1.tgz", + "integrity": "sha512-iSl/uNWjKbVPb6ll12SmHG9iGcC3oN8jjzdycm/mD3H/d8DLMloEiaz8lHQnsYCaPiNKwap1ThKlPvnKOU4SNg==", "cpu": [ "arm64" ], @@ -2188,9 +2352,9 @@ } }, "node_modules/@sentry/cli-linux-i686": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.33.1.tgz", - "integrity": "sha512-g2LS4oPXkPWOfKWukKzYp4FnXVRRSwBxhuQ9eSw2peeb58ZIObr4YKGOA/8HJRGkooBJIKGaAR2mH2Pk1TKaiA==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.34.1.tgz", + "integrity": "sha512-jq5o49pgzJFv/CQtvx4FLVO1xra22gzP76FtmvPwEhZQhJT6QduW9fpnvVDnOaY8YLzC7GAeszUV6sqZ0MZUqg==", "cpu": [ "x86", "ia32" @@ -2207,9 +2371,9 @@ } }, "node_modules/@sentry/cli-linux-x64": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.33.1.tgz", - "integrity": "sha512-IV3dcYV/ZcvO+VGu9U6kuxSdbsV2kzxaBwWUQxtzxJ+cOa7J8Hn1t0koKGtU53JVZNBa06qJWIcqgl4/pCuKIg==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.34.1.tgz", + "integrity": "sha512-O99RAkrcMErWLPRdza6HaG7kmHCx9MYFNDX6FLrAgSP3oz+X3ral1oDTIrMs4hVbPDK287ZGAqCJtk+1iOjEBg==", "cpu": [ "x64" ], @@ -2225,9 +2389,9 @@ } }, "node_modules/@sentry/cli-win32-i686": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.33.1.tgz", - "integrity": "sha512-F7cJySvkpzIu7fnLKNHYwBzZYYwlhoDbAUnaFX0UZCN+5DNp/5LwTp37a5TWOsmCaHMZT4i9IO4SIsnNw16/zQ==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.34.1.tgz", + "integrity": "sha512-yEeuneEVmExCbWlnSauhIg8wZDfKxRaou8XRfM6oPlSBu0XO5HUI3uRK5t2xT0zX8Syzh2kCZpdVE1KLavVeKA==", "cpu": [ "x86", "ia32" @@ -2243,9 +2407,9 @@ } }, "node_modules/@sentry/cli-win32-x64": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.33.1.tgz", - "integrity": "sha512-8VyRoJqtb2uQ8/bFRKNuACYZt7r+Xx0k2wXRGTyH05lCjAiVIXn7DiS2BxHFty7M1QEWUCMNsb/UC/x/Cu2wuA==", + "version": "2.34.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.34.1.tgz", + "integrity": "sha512-mU48VpDTwRgt7/Pf3vk/P87m4kM3XEXHHHfq9EvHCTspFF6GtMfL9njZ7+5Z+7ko852JS4kpunjZtsxmoP4/zA==", "cpu": [ "x64" ], @@ -2343,13 +2507,13 @@ } }, "node_modules/@sentry/vite-plugin": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-2.22.2.tgz", - "integrity": "sha512-LJSNTw75UJq77v2jCan8cRh0w1u6W30jxQsbqF7YyyhhfjPTyFUXYday9RDDe84qDEnspbZmgeTSWTvaTGsBRg==", + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-2.22.3.tgz", + "integrity": "sha512-+5bsLFRKOZzBp68XigoNE1pJ3tJ4gt2jXluApu54ui0N/yjfqGQ7LQTD7nL4tmJvB5Agwi0e7M7+fcxe9gSgBA==", "dev": true, "license": "MIT", "dependencies": { - "@sentry/bundler-plugin-core": "2.22.2", + "@sentry/bundler-plugin-core": "2.22.3", "unplugin": "1.0.1" }, "engines": { @@ -2366,9 +2530,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.52.0.tgz", - "integrity": "sha512-U1DOEgltjUwalN6uWYTewSnA14b+tE7lSylOiASKCAO61ENJeCq9VVD/TXHA6O5u9+6v5+UgGYBSccTKDoyMqw==", + "version": "5.53.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.1.tgz", + "integrity": "sha512-mvLG7s4Zy3Yvc2LsKm8BVafbmPrsReKgqwhmp4XKVmRW9us3KbWRqu3qBBfhVavcUUEHfNK7PvpTchvQpVdFpw==", "license": "MIT", "funding": { "type": "github", @@ -2376,12 +2540,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.52.1.tgz", - "integrity": "sha512-soyn4dNIUZ8US8NaPVXv06gkZFHaZnPfKWPDjRJjFRW3Y7WZ0jx72eT6zhw3VQlkMPysmXye8l35ewPHspKgbQ==", + "version": "5.53.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.53.1.tgz", + "integrity": "sha512-35HU4836Ey1/W74BxmS8p9KHXcDRGPdkw6w3VX0Tc5S9v5acFl80oi/yc6nsmoLhu68wQkWMyX0h7y7cOtY5OA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.52.0" + "@tanstack/query-core": "5.53.1" }, "funding": { "type": "github", @@ -2538,9 +2702,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.16.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", - "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", + "version": "20.16.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz", + "integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2573,9 +2737,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", - "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -3090,9 +3254,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -3233,9 +3397,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001651", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "version": "1.0.30001655", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", + "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", "dev": true, "funding": [ { @@ -3840,9 +4004,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -4692,6 +4856,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.1.tgz", "integrity": "sha512-IGrgWLuip4O2nq5CugXy4GI2V8kx4sFVy5Hd4vF7AR2gxS0N9s7nEAVUyeMtZKZvzrxVsHt73XdTsno1tClIkQ==", + "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.2.0", @@ -5690,9 +5855,9 @@ } }, "node_modules/mdast-util-mdx-jsx": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.2.tgz", - "integrity": "sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -5705,7 +5870,6 @@ "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^5.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" }, @@ -6919,9 +7083,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.42", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz", + "integrity": "sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==", "funding": [ { "type": "opencollective", @@ -7576,6 +7740,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", + "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" @@ -7818,9 +7983,9 @@ } }, "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz", + "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==", "license": "MIT", "dependencies": { "@types/estree": "1.0.5" @@ -7833,22 +7998,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", + "@rollup/rollup-android-arm-eabi": "4.21.2", + "@rollup/rollup-android-arm64": "4.21.2", + "@rollup/rollup-darwin-arm64": "4.21.2", + "@rollup/rollup-darwin-x64": "4.21.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.2", + "@rollup/rollup-linux-arm-musleabihf": "4.21.2", + "@rollup/rollup-linux-arm64-gnu": "4.21.2", + "@rollup/rollup-linux-arm64-musl": "4.21.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2", + "@rollup/rollup-linux-riscv64-gnu": "4.21.2", + "@rollup/rollup-linux-s390x-gnu": "4.21.2", + "@rollup/rollup-linux-x64-gnu": "4.21.2", + "@rollup/rollup-linux-x64-musl": "4.21.2", + "@rollup/rollup-win32-arm64-msvc": "4.21.2", + "@rollup/rollup-win32-ia32-msvc": "4.21.2", + "@rollup/rollup-win32-x64-msvc": "4.21.2", "fsevents": "~2.3.2" } }, @@ -8115,9 +8280,9 @@ "license": "MIT" }, "node_modules/style-to-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz", - "integrity": "sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.7.tgz", + "integrity": "sha512-uSjr59G5u6fbxUfKbb8GcqMGT3Xs9v5IbPkjb0S16GyOeBLAzSRK0CixBv5YrYvzO6TDLzIS6QCn78tkqWngPw==", "license": "MIT", "dependencies": { "inline-style-parser": "0.2.3" @@ -8498,13 +8663,12 @@ } }, "node_modules/vfile": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.2.tgz", - "integrity": "sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" }, "funding": { diff --git a/frontend/package.json b/frontend/package.json index a6e05228..d504175e 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -74,7 +74,7 @@ "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "^0.4.31" + "yorkie-js-sdk": "^0.4.27" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", From 01f57448f87768dcb09f12581d368feaedc9da47 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:36:08 +0900 Subject: [PATCH 03/20] Update `CHANGELOG.md` for `v0.1.8` (#328) --- CHANGELOG.md | 10 ++++++++++ backend/package-lock.json | 4 ++-- backend/package.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8df8653..870230af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and CodePair adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [0.1.8] - 2024-09-01 + +## Changed + +- Downgrade `yorkie-js-sdk` to `v0.4.27` by @devleejb in https://github.com/yorkie-team/codepair/pull/327 + +## Fixed + +- Disable retry logic for 401 errors during login by @devleejb in https://github.com/yorkie-team/codepair/pull/325 + ## [0.1.7] - 2024-08-29 ### Added diff --git a/backend/package-lock.json b/backend/package-lock.json index 16821203..98859a88 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-backend", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-backend", - "version": "0.1.7", + "version": "0.1.8", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-s3": "^3.509.0", diff --git a/backend/package.json b/backend/package.json index 18c167df..65120330 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "codepair-backend", - "version": "0.1.7", + "version": "0.1.8", "description": "CodePair Backend", "author": "yorkie-team", "private": true, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7f83b5b6..57f5c023 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-frontend", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-frontend", - "version": "0.1.7", + "version": "0.1.8", "license": "Apache-2.0", "dependencies": { "@codemirror/commands": "^6.3.3", diff --git a/frontend/package.json b/frontend/package.json index d504175e..1fb874ee 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,7 +2,7 @@ "name": "codepair-frontend", "description": "CodePair Frontend", "private": true, - "version": "0.1.7", + "version": "0.1.8", "type": "module", "author": "yorkie-team", "license": "Apache-2.0", From 9868b7ab449b7fbdd0523427231255f6889a60d5 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:19:56 +0900 Subject: [PATCH 04/20] Change version of `codemirror`, `yorkie-js-sdk` dependencies (#329) --- frontend/package-lock.json | 63 ++++++++++++++++++++------------------ frontend/package.json | 6 ++-- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 57f5c023..acfe3d7d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,9 +9,9 @@ "version": "0.1.8", "license": "Apache-2.0", "dependencies": { - "@codemirror/commands": "^6.3.3", + "@codemirror/commands": "6.1.2", "@codemirror/lang-markdown": "^6.2.3", - "@codemirror/view": "^6.23.0", + "@codemirror/view": "6.23.1", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^5.0.8", @@ -61,7 +61,7 @@ "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "^0.4.27" + "yorkie-js-sdk": "0.4.27" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", @@ -441,15 +441,15 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.1.tgz", - "integrity": "sha512-iBfKbyIoXS1FGdsKcZmnrxmbc8VcbMrSgD7AVrsnX+WyAYjmUDWvE93dt5D874qS4CCVu4O1JpbagHdXbbLiOw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.2.tgz", + "integrity": "sha512-sO3jdX1s0pam6lIdeSJLMN3DQ6mPEbM4yLvyKkdqtmd/UDwhXA5+AwFJ89rRXm6vTeOXBsE5cAmlos/t7MJdgg==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.27.0", - "@lezer/common": "^1.1.0" + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" } }, "node_modules/@codemirror/lang-css": { @@ -555,9 +555,9 @@ "license": "MIT" }, "node_modules/@codemirror/view": { - "version": "6.33.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.33.0.tgz", - "integrity": "sha512-AroaR3BvnjRW8fiZBalAaK+ZzB5usGgI014YKElYZvQdNH5ZIidHlO+cyf/2rWzyBFRkvG6VhiXeAEbC53P2YQ==", + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.1.tgz", + "integrity": "sha512-J2Xnn5lFYT1ZN/5ewEoMBCmLlL71lZ3mBdb7cUEuHhX2ESoSrNEucpsDXpX22EuTGm9LOgC9v4Z0wx+Ez8QmGA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.4.0", @@ -2530,9 +2530,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.53.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.1.tgz", - "integrity": "sha512-mvLG7s4Zy3Yvc2LsKm8BVafbmPrsReKgqwhmp4XKVmRW9us3KbWRqu3qBBfhVavcUUEHfNK7PvpTchvQpVdFpw==", + "version": "5.53.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.2.tgz", + "integrity": "sha512-gCsABpRrYfLsmwcQ0JCE5I3LOQ9KYrDDSnseUDP3T7ukV8E7+lhlHDJS4Gegt1TSZCsxKhc1J5A7TkF5ePjDUQ==", "license": "MIT", "funding": { "type": "github", @@ -2540,12 +2540,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.53.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.53.1.tgz", - "integrity": "sha512-35HU4836Ey1/W74BxmS8p9KHXcDRGPdkw6w3VX0Tc5S9v5acFl80oi/yc6nsmoLhu68wQkWMyX0h7y7cOtY5OA==", + "version": "5.53.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.53.2.tgz", + "integrity": "sha512-ZxG/rspElkfqg2LElnNtsNgPtiCZ4Wl2XY43bATQqPvNgyrhzbCFzCjDwSQy9fJhSiDVALSlxYS8YOIiToqQmg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.53.1" + "@tanstack/query-core": "5.53.2" }, "funding": { "type": "github", @@ -7083,9 +7083,9 @@ } }, "node_modules/postcss": { - "version": "8.4.42", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz", - "integrity": "sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==", + "version": "8.4.43", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.43.tgz", + "integrity": "sha512-gJAQVYbh5R3gYm33FijzCZj7CHyQ3hWMgJMprLUlIYqCwTeZhBQ19wp0e9mA25BUbEvY5+EXuuaAjqQsrBxQBQ==", "funding": [ { "type": "opencollective", @@ -7264,9 +7264,9 @@ } }, "node_modules/react-hook-form-mui": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/react-hook-form-mui/-/react-hook-form-mui-7.0.1.tgz", - "integrity": "sha512-ik4upg0pc3K9VTJcKSxmGXfwwPVoDLA+mPtD+csF47h1tYx4rrrRBpJ/27fvmzBPhUZS3pFAo7rs1LgMQ94Ukw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-hook-form-mui/-/react-hook-form-mui-7.2.0.tgz", + "integrity": "sha512-F7SAOQ7EGqOaFtiLaGZxDvwXHT4DerTLb6V+jBM3hVdI2/2zDWPA4tkJo1WZSaaUa1YZsTLA0hI3jubru1Aktg==", "license": "MIT", "workspaces": [ "apps/*", @@ -7276,8 +7276,8 @@ "node": ">=14" }, "peerDependencies": { - "@mui/icons-material": ">= 5.x <6", - "@mui/material": ">= 5.x <6", + "@mui/icons-material": ">= 5.x <7", + "@mui/material": ">= 5.x <7", "@mui/x-date-pickers": ">=7.0.0 <8", "react": ">=17 <19", "react-hook-form": ">=7.33.1" @@ -8885,10 +8885,13 @@ } }, "node_modules/yorkie-js-sdk": { - "version": "0.4.31", - "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.4.31.tgz", - "integrity": "sha512-X++xD4BvUvV5+Qx2AoItmzqUfpZhINRDurGaXGaWK93tSrgNGkNL938FYb2MuvA9D1QzphKLxb2p/nb7BSb9cw==", + "version": "0.4.27", + "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.4.27.tgz", + "integrity": "sha512-UVFXIJDg3Or8H95IoEYhebKLh8wy2thYH0IoTJzoMtJwa/EmVvNjktrCYIuovWIBa/e3QFbhQbpw+CQ7oeLBzg==", "license": "Apache-2.0", + "workspaces": [ + "examples/*" + ], "dependencies": { "@bufbuild/protobuf": "^1.6.0", "@connectrpc/connect": "^1.4.0", diff --git a/frontend/package.json b/frontend/package.json index 1fb874ee..607f5385 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,9 +22,9 @@ ] }, "dependencies": { - "@codemirror/commands": "^6.3.3", + "@codemirror/commands": "6.1.2", "@codemirror/lang-markdown": "^6.2.3", - "@codemirror/view": "^6.23.0", + "@codemirror/view": "6.23.1", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^5.0.8", @@ -74,7 +74,7 @@ "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "^0.4.27" + "yorkie-js-sdk": "0.4.27" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", From ef94119ac2e791f5c0b43bbdbc169622c912711d Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:23:46 +0900 Subject: [PATCH 05/20] Update `CHANGELOG.md` for `v0.1.9` (#330) --- CHANGELOG.md | 6 ++++++ backend/package-lock.json | 4 ++-- backend/package.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 870230af..cf7843bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and CodePair adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [0.1.9] - 20204-09-02 + +## Changed + +- Change version of `codemirror`, `yorkie-js-sdk` dependencies by @devleejb in https://github.com/yorkie-team/codepair/pull/329 + ## [0.1.8] - 2024-09-01 ## Changed diff --git a/backend/package-lock.json b/backend/package-lock.json index 98859a88..fa2569f2 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-backend", - "version": "0.1.8", + "version": "0.1.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-backend", - "version": "0.1.8", + "version": "0.1.9", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-s3": "^3.509.0", diff --git a/backend/package.json b/backend/package.json index 65120330..68f4b15f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "codepair-backend", - "version": "0.1.8", + "version": "0.1.9", "description": "CodePair Backend", "author": "yorkie-team", "private": true, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index acfe3d7d..33cb103c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-frontend", - "version": "0.1.8", + "version": "0.1.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-frontend", - "version": "0.1.8", + "version": "0.1.9", "license": "Apache-2.0", "dependencies": { "@codemirror/commands": "6.1.2", diff --git a/frontend/package.json b/frontend/package.json index 607f5385..dff6ed87 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,7 +2,7 @@ "name": "codepair-frontend", "description": "CodePair Frontend", "private": true, - "version": "0.1.8", + "version": "0.1.9", "type": "module", "author": "yorkie-team", "license": "Apache-2.0", From 3682417440c7b890117a071792a18be4947a7127 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:37:48 +0900 Subject: [PATCH 06/20] Update `yorkie-js-sdk` to 0.4.31 (#331) --- backend/docker/docker-compose-full.yml | 2 +- backend/docker/docker-compose.yml | 2 +- frontend/package-lock.json | 11 ++++------- frontend/package.json | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/backend/docker/docker-compose-full.yml b/backend/docker/docker-compose-full.yml index de021427..a42dc14e 100644 --- a/backend/docker/docker-compose-full.yml +++ b/backend/docker/docker-compose-full.yml @@ -34,7 +34,7 @@ services: - "yorkie:yorkie" yorkie: - image: "yorkieteam/yorkie:0.4.27" + image: "yorkieteam/yorkie:0.4.31" command: ["server", "--enable-pprof"] restart: always ports: diff --git a/backend/docker/docker-compose.yml b/backend/docker/docker-compose.yml index cc3521dc..3693c2fa 100644 --- a/backend/docker/docker-compose.yml +++ b/backend/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: yorkie: - image: "yorkieteam/yorkie:0.4.27" + image: "yorkieteam/yorkie:0.4.31" command: ["server", "--enable-pprof"] restart: always ports: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 33cb103c..10bf09b9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -61,7 +61,7 @@ "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "0.4.27" + "yorkie-js-sdk": "0.4.31" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", @@ -8885,13 +8885,10 @@ } }, "node_modules/yorkie-js-sdk": { - "version": "0.4.27", - "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.4.27.tgz", - "integrity": "sha512-UVFXIJDg3Or8H95IoEYhebKLh8wy2thYH0IoTJzoMtJwa/EmVvNjktrCYIuovWIBa/e3QFbhQbpw+CQ7oeLBzg==", + "version": "0.4.31", + "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.4.31.tgz", + "integrity": "sha512-X++xD4BvUvV5+Qx2AoItmzqUfpZhINRDurGaXGaWK93tSrgNGkNL938FYb2MuvA9D1QzphKLxb2p/nb7BSb9cw==", "license": "Apache-2.0", - "workspaces": [ - "examples/*" - ], "dependencies": { "@bufbuild/protobuf": "^1.6.0", "@connectrpc/connect": "^1.4.0", diff --git a/frontend/package.json b/frontend/package.json index dff6ed87..ba84ecd4 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -74,7 +74,7 @@ "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "0.4.27" + "yorkie-js-sdk": "0.4.31" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", From 048529308fe0c354d9af739e0a1e54760be9a513 Mon Sep 17 00:00:00 2001 From: choidabom <48302257+choidabom@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:16:54 +0900 Subject: [PATCH 07/20] Fix user nickname display in shared document (#333) --- .../src/pages/workspace/document/share/Index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/workspace/document/share/Index.tsx b/frontend/src/pages/workspace/document/share/Index.tsx index f8efbd40..365da099 100644 --- a/frontend/src/pages/workspace/document/share/Index.tsx +++ b/frontend/src/pages/workspace/document/share/Index.tsx @@ -1,19 +1,24 @@ import { Box } from "@mui/material"; +import { useEffect, useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { Navigate, useLocation, useSearchParams } from "react-router-dom"; import DocumentView from "../../../../components/editor/DocumentView"; import { useGetDocumentBySharingTokenQuery } from "../../../../hooks/api/document"; -import { Navigate, useLocation, useSearchParams } from "react-router-dom"; -import { useEffect, useMemo } from "react"; import { useYorkieDocument } from "../../../../hooks/useYorkieDocument"; -import { useDispatch } from "react-redux"; import { setClient, setDoc, setMode, setShareRole } from "../../../../store/editorSlice"; +import { selectUser } from "../../../../store/userSlice"; function DocumentShareIndex() { const dispatch = useDispatch(); const location = useLocation(); const [searchParams] = useSearchParams(); + const userStore = useSelector(selectUser); const shareToken = useMemo(() => searchParams.get("token"), [searchParams]); const { data: sharedDocument } = useGetDocumentBySharingTokenQuery(shareToken); - const { doc, client } = useYorkieDocument(sharedDocument?.yorkieDocumentId, "Anonymous"); + const { doc, client } = useYorkieDocument( + sharedDocument?.yorkieDocumentId, + userStore.data?.nickname ?? "Anonymous" + ); useEffect(() => { if (!sharedDocument?.role) return; From ba3d54d82278973d0bc3270806459ee4e3a3eb90 Mon Sep 17 00:00:00 2001 From: choidabom <48302257+choidabom@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:44:59 +0900 Subject: [PATCH 08/20] Add hyperlink creation feature (#332) * Add useToolBar hook * Add hyperlink creation feature * Add text selection check before inserting hyperlink * Refactor `useToolBar` & extract `checkAndAddFormat` functon to a separate utility file * Change valid URL check regular expression to `validator` library * Fix missing validator package installation --- frontend/package-lock.json | 16 ++++ frontend/package.json | 2 + frontend/src/components/editor/Editor.tsx | 98 ++++------------------ frontend/src/hooks/useFormatUtils.ts | 16 ++++ frontend/src/hooks/useToolBar.ts | 60 +++++++++++++ frontend/src/utils/urlHyperlinkInserter.ts | 45 ++++++++++ 6 files changed, 156 insertions(+), 81 deletions(-) create mode 100644 frontend/src/hooks/useToolBar.ts create mode 100644 frontend/src/utils/urlHyperlinkInserter.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 10bf09b9..864bf7a3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -60,6 +60,7 @@ "rehype-rewrite": "^4.0.2", "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", + "validator": "^13.12.0", "vite-plugin-package-version": "^1.1.0", "yorkie-js-sdk": "0.4.31" }, @@ -74,6 +75,7 @@ "@types/react-dom": "^18.2.17", "@types/react-infinite-scroller": "^1.2.5", "@types/react-scroll-sync": "^0.9.0", + "@types/validator": "^13.12.1", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", "@vitejs/plugin-react": "^4.2.1", @@ -2804,6 +2806,12 @@ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", "license": "MIT" }, + "node_modules/@types/validator": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.1.tgz", + "integrity": "sha512-w0URwf7BQb0rD/EuiG12KP0bailHKHP5YVviJG9zw3ykAokL0TuxU2TUqMB7EwZ59bDHYdeTIvjI5m0S7qHfOA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -8662,6 +8670,14 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index ba84ecd4..bd8acfab 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -73,6 +73,7 @@ "rehype-rewrite": "^4.0.2", "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", + "validator": "^13.12.0", "vite-plugin-package-version": "^1.1.0", "yorkie-js-sdk": "0.4.31" }, @@ -87,6 +88,7 @@ "@types/react-dom": "^18.2.17", "@types/react-infinite-scroller": "^1.2.5", "@types/react-scroll-sync": "^0.9.0", + "@types/validator": "^13.12.1", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", "@vitejs/plugin-react": "^4.2.1", diff --git a/frontend/src/components/editor/Editor.tsx b/frontend/src/components/editor/Editor.tsx index 2682c81e..31f9cae4 100644 --- a/frontend/src/components/editor/Editor.tsx +++ b/frontend/src/components/editor/Editor.tsx @@ -1,6 +1,6 @@ import { markdown } from "@codemirror/lang-markdown"; import { EditorState } from "@codemirror/state"; -import { keymap, ViewUpdate } from "@codemirror/view"; +import { keymap } from "@codemirror/view"; import { xcodeDark, xcodeLight } from "@uiw/codemirror-theme-xcode"; import { basicSetup, EditorView } from "codemirror"; import { useCallback, useEffect, useState } from "react"; @@ -8,12 +8,14 @@ import { useDispatch, useSelector } from "react-redux"; import { ScrollSyncPane } from "react-scroll-sync"; import { useCreateUploadUrlMutation, useUploadFileMutation } from "../../hooks/api/file"; import { useCurrentTheme } from "../../hooks/useCurrentTheme"; -import { FormatType, ToolBarState, useFormatUtils } from "../../hooks/useFormatUtils"; +import { useFormatUtils } from "../../hooks/useFormatUtils"; +import { useToolBar } from "../../hooks/useToolBar"; import { selectEditor, setCmView } from "../../store/editorSlice"; import { selectSetting } from "../../store/settingSlice"; import { selectWorkspace } from "../../store/workspaceSlice"; import { imageUploader } from "../../utils/imageUploader"; import { intelligencePivot } from "../../utils/intelligence/intelligencePivot"; +import { urlHyperlinkInserter } from "../../utils/urlHyperlinkInserter"; import { yorkieCodeMirror } from "../../utils/yorkie"; import ToolBar from "./ToolBar"; @@ -26,77 +28,15 @@ function Editor() { const workspaceStore = useSelector(selectWorkspace); const { mutateAsync: createUploadUrl } = useCreateUploadUrlMutation(); const { mutateAsync: uploadFile } = useUploadFileMutation(); - - const [toolBarState, setToolBarState] = useState({ - show: false, - position: { top: 0, left: 0 }, - selectedFormats: new Set(), - }); - - const { getFormatMarkerLength, applyFormat, setKeymapConfig } = useFormatUtils(); + const { applyFormat, setKeymapConfig } = useFormatUtils(); + const { toolBarState, setToolBarState, updateFormatBar } = useToolBar(); const ref = useCallback((node: HTMLElement | null) => { if (!node) return; setElement(node); }, []); - const updateFormatBar = useCallback( - (update: ViewUpdate) => { - const selection = update.state.selection.main; - if (!selection.empty) { - const coords = update.view.coordsAtPos(selection.from); - if (coords) { - const maxLength = getFormatMarkerLength(update.view.state, selection.from); - - const selectedTextStart = update.state.sliceDoc( - selection.from - maxLength, - selection.from - ); - const selectedTextEnd = update.state.sliceDoc( - selection.to, - selection.to + maxLength - ); - const formats = new Set(); - - const checkAndAddFormat = (marker: string, format: FormatType) => { - if ( - selectedTextStart.includes(marker) && - selectedTextEnd.includes(marker) - ) { - formats.add(format); - } - }; - - checkAndAddFormat("**", FormatType.BOLD); - checkAndAddFormat("_", FormatType.ITALIC); - checkAndAddFormat("`", FormatType.CODE); - checkAndAddFormat("~~", FormatType.STRIKETHROUGH); - - // TODO: Modify the rendering method so that it is not affected by the size of the Toolbar - setToolBarState((prev) => ({ - ...prev, - show: true, - position: { - top: coords.top - 5, - left: coords.left, - }, - selectedFormats: formats, - })); - } - } else { - setToolBarState((prev) => ({ - ...prev, - show: false, - selectedFormats: new Set(), - })); - } - }, - [getFormatMarkerLength] - ); - useEffect(() => { - let view: EditorView | undefined = undefined; - if ( !element || !editorStore.doc || @@ -126,28 +66,24 @@ function Editor() { keymap.of(setKeymapConfig()), basicSetup, markdown(), - yorkieCodeMirror(editorStore.doc, editorStore.client), themeMode == "light" ? xcodeLight : xcodeDark, - EditorView.theme({ - "&": { width: "100%" }, - }), + EditorView.theme({ "&": { width: "100%" } }), EditorView.lineWrapping, - intelligencePivot, - ...(settingStore.fileUpload.enable - ? [imageUploader(handleUploadImage, editorStore.doc)] - : []), EditorView.updateListener.of((update) => { if (update.selectionSet) { updateFormatBar(update); } }), + yorkieCodeMirror(editorStore.doc, editorStore.client), + intelligencePivot, + ...(settingStore.fileUpload.enable + ? [imageUploader(handleUploadImage, editorStore.doc)] + : []), + urlHyperlinkInserter(editorStore.doc), ], }); - view = new EditorView({ - state, - parent: element, - }); + const view = new EditorView({ state, parent: element }); dispatch(setCmView(view)); @@ -155,15 +91,15 @@ function Editor() { view?.destroy(); }; }, [ - dispatch, + element, editorStore.client, editorStore.doc, - element, themeMode, workspaceStore.data, + settingStore.fileUpload?.enable, + dispatch, createUploadUrl, uploadFile, - settingStore.fileUpload?.enable, applyFormat, updateFormatBar, setKeymapConfig, diff --git a/frontend/src/hooks/useFormatUtils.ts b/frontend/src/hooks/useFormatUtils.ts index deedf1e0..8816cba5 100644 --- a/frontend/src/hooks/useFormatUtils.ts +++ b/frontend/src/hooks/useFormatUtils.ts @@ -143,10 +143,26 @@ export const useFormatUtils = () => { [cmView, applyFormat] ); + const checkAndAddFormat = useCallback( + ( + selectedTextStart: string, + selectedTextEnd: string, + marker: string, + format: FormatType, + formats: Set + ) => { + if (selectedTextStart.includes(marker) && selectedTextEnd.includes(marker)) { + formats.add(format); + } + }, + [] + ); + return { getFormatMarkerLength, applyFormat, setKeymapConfig, toggleButtonChangeHandler, + checkAndAddFormat, }; }; diff --git a/frontend/src/hooks/useToolBar.ts b/frontend/src/hooks/useToolBar.ts new file mode 100644 index 00000000..fcbffecc --- /dev/null +++ b/frontend/src/hooks/useToolBar.ts @@ -0,0 +1,60 @@ +import { useCallback, useState } from "react"; +import { FormatType, ToolBarState, useFormatUtils } from "./useFormatUtils"; +import { ViewUpdate } from "@codemirror/view"; + +export const useToolBar = () => { + const [toolBarState, setToolBarState] = useState({ + show: false, + position: { top: 0, left: 0 }, + selectedFormats: new Set(), + }); + const { getFormatMarkerLength, checkAndAddFormat } = useFormatUtils(); + + const updateFormatBar = useCallback( + (update: ViewUpdate) => { + const { state, view } = update; + const selection = state.selection.main; + + if (selection.empty) { + setToolBarState((prev) => ({ + ...prev, + show: false, + selectedFormats: new Set(), + })); + return; + } + + const coords = view.coordsAtPos(selection.from); + if (!coords) return; + + const maxLength = getFormatMarkerLength(view.state, selection.from); + const selectedTextStart = state.sliceDoc(selection.from - maxLength, selection.from); + const selectedTextEnd = state.sliceDoc(selection.to, selection.to + maxLength); + const formats = new Set(); + const formatChecks = [ + { marker: "**", format: FormatType.BOLD }, + { marker: "_", format: FormatType.ITALIC }, + { marker: "`", format: FormatType.CODE }, + { marker: "~~", format: FormatType.STRIKETHROUGH }, + ]; + + formatChecks.forEach(({ marker, format }) => { + checkAndAddFormat(selectedTextStart, selectedTextEnd, marker, format, formats); + }); + + // TODO: Modify the rendering method so that it is not affected by the size of the Toolbar + setToolBarState((prev) => ({ + ...prev, + show: true, + position: { + top: coords.top - 5, + left: coords.left, + }, + selectedFormats: formats, + })); + }, + [getFormatMarkerLength, checkAndAddFormat] + ); + + return { toolBarState, setToolBarState, updateFormatBar }; +}; diff --git a/frontend/src/utils/urlHyperlinkInserter.ts b/frontend/src/utils/urlHyperlinkInserter.ts new file mode 100644 index 00000000..961f29b6 --- /dev/null +++ b/frontend/src/utils/urlHyperlinkInserter.ts @@ -0,0 +1,45 @@ +import { EditorView } from "codemirror"; +import validator from "validator"; +import { CodePairDocType } from "../store/editorSlice"; + +const isValidUrl = (url: string) => { + return validator.isURL(url); +}; + +const insertLinkToEditor = (url: string, view: EditorView, doc: CodePairDocType) => { + const { from, to } = view.state.selection.main; + const selectedText = view.state.sliceDoc(from, to); + const insert = `[${selectedText}](${url})`; + + doc.update((root, presence) => { + root.content.edit(from, to, insert); + presence.set({ + selection: root.content.indexRangeToPosRange([ + from + insert.length, + from + insert.length, + ]), + }); + }); + + view.dispatch({ + changes: { from, to, insert }, + selection: { + anchor: from + insert.length, + }, + }); +}; + +export const urlHyperlinkInserter = (doc: CodePairDocType) => { + return EditorView.domEventHandlers({ + paste(event, view) { + const url = event.clipboardData?.getData("text/plain"); + if (!url || !isValidUrl(url)) return; + + const { from, to } = view.state.selection.main; + if (from === to) return; + + insertLinkToEditor(url, view, doc); + event.preventDefault(); + }, + }); +}; From 9ece2bde41313d87ef276dd71126adfd953b076e Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:39:15 +0900 Subject: [PATCH 09/20] Bump up `yorkie-js-sdk` to `0.5.0` (#334) --- backend/docker/docker-compose-full.yml | 2 +- backend/docker/docker-compose.yml | 2 +- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/docker/docker-compose-full.yml b/backend/docker/docker-compose-full.yml index a42dc14e..77d6e094 100644 --- a/backend/docker/docker-compose-full.yml +++ b/backend/docker/docker-compose-full.yml @@ -34,7 +34,7 @@ services: - "yorkie:yorkie" yorkie: - image: "yorkieteam/yorkie:0.4.31" + image: "yorkieteam/yorkie:0.5.0" command: ["server", "--enable-pprof"] restart: always ports: diff --git a/backend/docker/docker-compose.yml b/backend/docker/docker-compose.yml index 3693c2fa..f5c24d5f 100644 --- a/backend/docker/docker-compose.yml +++ b/backend/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: yorkie: - image: "yorkieteam/yorkie:0.4.31" + image: "yorkieteam/yorkie:0.5.0" command: ["server", "--enable-pprof"] restart: always ports: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 864bf7a3..fffa3497 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -62,7 +62,7 @@ "remark-math": "^6.0.0", "validator": "^13.12.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "0.4.31" + "yorkie-js-sdk": "0.5.0" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", @@ -8901,9 +8901,9 @@ } }, "node_modules/yorkie-js-sdk": { - "version": "0.4.31", - "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.4.31.tgz", - "integrity": "sha512-X++xD4BvUvV5+Qx2AoItmzqUfpZhINRDurGaXGaWK93tSrgNGkNL938FYb2MuvA9D1QzphKLxb2p/nb7BSb9cw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.5.0.tgz", + "integrity": "sha512-TE+FJEjtB7cR501XoRypzyWNnUXPMbMC+gaosmEne6VOSfPgs5CivaSdg0Jmf7moMBTwptFxKq5mgUwxEd5UOQ==", "license": "Apache-2.0", "dependencies": { "@bufbuild/protobuf": "^1.6.0", diff --git a/frontend/package.json b/frontend/package.json index bd8acfab..2f1647b2 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -75,7 +75,7 @@ "remark-math": "^6.0.0", "validator": "^13.12.0", "vite-plugin-package-version": "^1.1.0", - "yorkie-js-sdk": "0.4.31" + "yorkie-js-sdk": "0.5.0" }, "devDependencies": { "@sentry/vite-plugin": "^2.20.1", From 95edac3400ccfb14555f5dc51352c34c64208e49 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:47:44 +0900 Subject: [PATCH 10/20] Update `CHANGELOG.md` for `v0.1.10` (#335) --- CHANGELOG.md | 18 +++++++++++++++--- backend/package-lock.json | 4 ++-- backend/package.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf7843bd..1dd7c535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,19 +7,31 @@ and CodePair adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [0.1.10] - 20204-09-05 + +### Added + +- Add hyperlink creation feature by @choidabom in https://github.com/yorkie-team/codepair/pull/332 + +### Changed + +- Update `yorkie-js-sdk` to 0.4.31 by @devleejb in https://github.com/yorkie-team/codepair/pull/331 +- Ensure correct representation of logged-in users in shared document view by @choidabom in https://github.com/yorkie-team/codepair/pull/333 +- Bump up `yorkie-js-sdk` to `0.5.0` by @devleejb in https://github.com/yorkie-team/codepair/pull/334 + ## [0.1.9] - 20204-09-02 -## Changed +### Changed - Change version of `codemirror`, `yorkie-js-sdk` dependencies by @devleejb in https://github.com/yorkie-team/codepair/pull/329 ## [0.1.8] - 2024-09-01 -## Changed +### Changed - Downgrade `yorkie-js-sdk` to `v0.4.27` by @devleejb in https://github.com/yorkie-team/codepair/pull/327 -## Fixed +### Fixed - Disable retry logic for 401 errors during login by @devleejb in https://github.com/yorkie-team/codepair/pull/325 diff --git a/backend/package-lock.json b/backend/package-lock.json index fa2569f2..1b130271 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-backend", - "version": "0.1.9", + "version": "0.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-backend", - "version": "0.1.9", + "version": "0.1.10", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-s3": "^3.509.0", diff --git a/backend/package.json b/backend/package.json index 68f4b15f..5a021e43 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "codepair-backend", - "version": "0.1.9", + "version": "0.1.10", "description": "CodePair Backend", "author": "yorkie-team", "private": true, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fffa3497..d6ba95e8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-frontend", - "version": "0.1.9", + "version": "0.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-frontend", - "version": "0.1.9", + "version": "0.1.10", "license": "Apache-2.0", "dependencies": { "@codemirror/commands": "6.1.2", diff --git a/frontend/package.json b/frontend/package.json index 2f1647b2..6f83a6f1 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,7 +2,7 @@ "name": "codepair-frontend", "description": "CodePair Frontend", "private": true, - "version": "0.1.9", + "version": "0.1.10", "type": "module", "author": "yorkie-team", "license": "Apache-2.0", From 94f2a63a6c9b65d4aabb3b02c277cea3986e4a9f Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Sat, 7 Sep 2024 13:43:24 +0900 Subject: [PATCH 11/20] Change the backend environment for LangSmith (#336) --- backend/.env.development | 7 +------ backend/docker/docker-compose-full.yml | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/backend/.env.development b/backend/.env.development index 8c8d82a1..2df84ab8 100644 --- a/backend/.env.development +++ b/backend/.env.development @@ -51,13 +51,8 @@ OPENAI_API_KEY=your_openai_api_key_here # LANGCHAIN_TRACING_V2: Whether LangSmith monitoring for YorkieIntelligence is needed. # Set to true if LangSmith monitoring is required. -# If set to false, LANGCHAIN_ENDPOINT, LANGCHAIN_API_KEY, and LANGCHAIN_PROJECT are not required. +# If set to false, LANGCHAIN_API_KEY, and LANGCHAIN_PROJECT are not required. LANGCHAIN_TRACING_V2=false -# LANGCHAIN_ENDPOINT: LangSmith API URL. -# This URL is used to communicate with LangSmith. -# Example: https://api.smith.langchain.com -# To obtain the LangSmith endpoint, visit LangSmith: https://www.langchain.com/langsmith -LANGCHAIN_ENDPOINT=https://www.langchain.com/langsmith # LANGCHAIN_API_KEY: LangSmith API Key. # This key is required for authenticating with LangSmith. # To obtain an API key, visit LangSmith: https://www.langchain.com/langsmith diff --git a/backend/docker/docker-compose-full.yml b/backend/docker/docker-compose-full.yml index 77d6e094..621534cd 100644 --- a/backend/docker/docker-compose-full.yml +++ b/backend/docker/docker-compose-full.yml @@ -7,6 +7,7 @@ services: environment: DATABASE_URL: "mongodb://mongo:27017/codepair" # Environment variables need to be passed to the container + # You can find the description of each environment variable in the backend/.env.development file GITHUB_CLIENT_ID: "your_github_client_id_here" GITHUB_CLIENT_SECRET: "your_github_client_secret_here" GITHUB_CALLBACK_URL: "http://localhost:3000/auth/login/github" @@ -19,7 +20,7 @@ services: YORKIE_INTELLIGENCE: "ollama:gemma2:2b" OLLAMA_HOST_URL: http://yorkie-intelligence:11434 OPENAI_API_KEY: "your_openai_api_key_here" - LANGCHAIN_ENDPOINT: "https://www.langchain.com/langsmith" + LANGCHAIN_TRACING_V2: "false" LANGCHAIN_API_KEY: "your_langsmith_api_key_here" LANGCHAIN_PROJECT: "your_langsmith_project_name_here" FILE_UPLOAD: false From 70beff740c88bf82036d3d84ac1cfdb197f80733 Mon Sep 17 00:00:00 2001 From: choidabom <48302257+choidabom@users.noreply.github.com> Date: Wed, 11 Sep 2024 23:14:50 +0900 Subject: [PATCH 12/20] Add Vim binding support using CodeMirror 6 (#340) * Add Vim binding for CodePair using CodeMirror 6 * Add vim key mapping 'jj' for insert mode * Fix argument type * Fix components to use `function` keyword * Add EditorBottomBar props interface * Fix enum members to use capital letters * Move `EditorBottomBar` into `Editor` components * Change to `Paper` component * Move codekey options to `configSlice` --- frontend/package-lock.json | 13 ++++ frontend/package.json | 1 + .../src/components/common/ThemeButton.tsx | 8 +- .../src/components/editor/DocumentView.tsx | 34 ++++++--- frontend/src/components/editor/Editor.tsx | 63 ++++++++++------ .../src/components/editor/EditorBottomBar.tsx | 74 +++++++++++++++++++ frontend/src/components/editor/Preview.tsx | 2 +- .../src/components/headers/DocumentHeader.tsx | 7 +- .../components/popovers/ProfilePopover.tsx | 16 ++-- .../pages/workspace/document/share/Index.tsx | 13 +++- frontend/src/store/configSlice.ts | 22 +++++- frontend/src/store/editorSlice.ts | 19 +++-- 12 files changed, 210 insertions(+), 62 deletions(-) create mode 100644 frontend/src/components/editor/EditorBottomBar.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d6ba95e8..d853b4fb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,6 +20,7 @@ "@mui/x-date-pickers": "^7.13.0", "@react-hook/window-size": "^3.1.1", "@reduxjs/toolkit": "^2.0.1", + "@replit/codemirror-vim": "^6.2.1", "@sentry/react": "^7.99.0", "@swc/helpers": "^0.5.3", "@tanstack/react-query": "^5.17.15", @@ -1973,6 +1974,18 @@ "node": ">=14.0.0" } }, + "node_modules/@replit/codemirror-vim": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.2.1.tgz", + "integrity": "sha512-qDAcGSHBYU5RrdO//qCmD8K9t6vbP327iCj/iqrkVnjbrpFhrjOt92weGXGHmTNRh16cUtkUZ7Xq7rZf+8HVow==", + "peerDependencies": { + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.1.0", + "@codemirror/search": "^6.2.0", + "@codemirror/state": "^6.0.1", + "@codemirror/view": "^6.0.3" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6f83a6f1..cdcd38dd 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "@mui/x-date-pickers": "^7.13.0", "@react-hook/window-size": "^3.1.1", "@reduxjs/toolkit": "^2.0.1", + "@replit/codemirror-vim": "^6.2.1", "@sentry/react": "^7.99.0", "@swc/helpers": "^0.5.3", "@tanstack/react-query": "^5.17.15", diff --git a/frontend/src/components/common/ThemeButton.tsx b/frontend/src/components/common/ThemeButton.tsx index c29a036c..6d8afe69 100644 --- a/frontend/src/components/common/ThemeButton.tsx +++ b/frontend/src/components/common/ThemeButton.tsx @@ -1,21 +1,21 @@ -import { IconButton } from "@mui/material"; import DarkModeIcon from "@mui/icons-material/DarkMode"; import LightModeIcon from "@mui/icons-material/LightMode"; +import { IconButton } from "@mui/material"; import { useDispatch } from "react-redux"; -import { setTheme } from "../../store/configSlice"; import { useCurrentTheme } from "../../hooks/useCurrentTheme"; +import { setTheme, ThemeType } from "../../store/configSlice"; function ThemeButton() { const dispatch = useDispatch(); const themeMode = useCurrentTheme(); const handleChangeTheme = () => { - dispatch(setTheme(themeMode == "light" ? "dark" : "light")); + dispatch(setTheme(themeMode == ThemeType.LIGHT ? ThemeType.DARK : ThemeType.LIGHT)); }; return ( - {themeMode === "light" ? : } + {themeMode === ThemeType.LIGHT ? : } ); } diff --git a/frontend/src/components/editor/DocumentView.tsx b/frontend/src/components/editor/DocumentView.tsx index 7b8613e4..1209458c 100644 --- a/frontend/src/components/editor/DocumentView.tsx +++ b/frontend/src/components/editor/DocumentView.tsx @@ -1,11 +1,11 @@ +import { Backdrop, Box, CircularProgress, Paper } from "@mui/material"; +import { useWindowWidth } from "@react-hook/window-size"; import { useSelector } from "react-redux"; -import { selectEditor } from "../../store/editorSlice"; import Resizable from "react-resizable-layout"; -import { useWindowWidth } from "@react-hook/window-size"; +import { ScrollSync, ScrollSyncPane } from "react-scroll-sync"; +import { EditorModeType, selectEditor } from "../../store/editorSlice"; import Editor from "./Editor"; -import { Backdrop, Box, CircularProgress, Paper } from "@mui/material"; import Preview from "./Preview"; -import { ScrollSync, ScrollSyncPane } from "react-scroll-sync"; function DocumentView() { const editorStore = useSelector(selectEditor); @@ -20,9 +20,7 @@ function DocumentView() { return ( <> - {/* For Markdown Preview Theme */} -
- {editorStore.mode === "both" && ( + {editorStore.mode === EditorModeType.BOTH && ( {({ position: width, separatorProps }) => ( @@ -32,10 +30,18 @@ function DocumentView() { display: "flex", height: "100%", overflow: "hidden", + position: "relative", }} > -
- +
+
)} - {editorStore.mode === "read" && ( + + {editorStore.mode === EditorModeType.EDIT && ( +
+ +
+ )} + + {editorStore.mode === EditorModeType.READ && ( )} - {editorStore.mode === "edit" && } ); } diff --git a/frontend/src/components/editor/Editor.tsx b/frontend/src/components/editor/Editor.tsx index 31f9cae4..1c6dfe29 100644 --- a/frontend/src/components/editor/Editor.tsx +++ b/frontend/src/components/editor/Editor.tsx @@ -1,6 +1,7 @@ import { markdown } from "@codemirror/lang-markdown"; import { EditorState } from "@codemirror/state"; import { keymap } from "@codemirror/view"; +import { Vim, vim } from "@replit/codemirror-vim"; import { xcodeDark, xcodeLight } from "@uiw/codemirror-theme-xcode"; import { basicSetup, EditorView } from "codemirror"; import { useCallback, useEffect, useState } from "react"; @@ -10,6 +11,7 @@ import { useCreateUploadUrlMutation, useUploadFileMutation } from "../../hooks/a import { useCurrentTheme } from "../../hooks/useCurrentTheme"; import { useFormatUtils } from "../../hooks/useFormatUtils"; import { useToolBar } from "../../hooks/useToolBar"; +import { CodeKeyType, selectConfig } from "../../store/configSlice"; import { selectEditor, setCmView } from "../../store/editorSlice"; import { selectSetting } from "../../store/settingSlice"; import { selectWorkspace } from "../../store/workspaceSlice"; @@ -17,13 +19,20 @@ import { imageUploader } from "../../utils/imageUploader"; import { intelligencePivot } from "../../utils/intelligence/intelligencePivot"; import { urlHyperlinkInserter } from "../../utils/urlHyperlinkInserter"; import { yorkieCodeMirror } from "../../utils/yorkie"; +import EditorBottomBar, { BOTTOM_BAR_HEIGHT } from "./EditorBottomBar"; import ToolBar from "./ToolBar"; -function Editor() { +interface EditorProps { + width: number | string; +} + +function Editor(props: EditorProps) { + const { width } = props; const dispatch = useDispatch(); const themeMode = useCurrentTheme(); const [element, setElement] = useState(); const editorStore = useSelector(selectEditor); + const configStore = useSelector(selectConfig); const settingStore = useSelector(selectSetting); const workspaceStore = useSelector(selectWorkspace); const { mutateAsync: createUploadUrl } = useCreateUploadUrlMutation(); @@ -66,6 +75,7 @@ function Editor() { keymap.of(setKeymapConfig()), basicSetup, markdown(), + configStore.codeKey === CodeKeyType.VIM ? vim() : [], themeMode == "light" ? xcodeLight : xcodeDark, EditorView.theme({ "&": { width: "100%" } }), EditorView.lineWrapping, @@ -83,8 +93,10 @@ function Editor() { ], }); - const view = new EditorView({ state, parent: element }); + // Vim key mapping: Map 'jj' to '' in insert mode + Vim.map("jj", "", "insert"); + const view = new EditorView({ state, parent: element }); dispatch(setCmView(view)); return () => { @@ -94,6 +106,7 @@ function Editor() { element, editorStore.client, editorStore.doc, + configStore.codeKey, themeMode, workspaceStore.data, settingStore.fileUpload?.enable, @@ -106,26 +119,34 @@ function Editor() { ]); return ( - -
-
- {Boolean(toolBarState.show) && ( - - )} + <> +
+ +
+
+ {Boolean(toolBarState.show) && ( + + )} +
+
-
+ + ); } diff --git a/frontend/src/components/editor/EditorBottomBar.tsx b/frontend/src/components/editor/EditorBottomBar.tsx new file mode 100644 index 00000000..b3e3d507 --- /dev/null +++ b/frontend/src/components/editor/EditorBottomBar.tsx @@ -0,0 +1,74 @@ +import { Button, Menu, MenuItem, Paper } from "@mui/material"; +import { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { CodeKeyType, selectConfig, setCodeKeyType } from "../../store/configSlice"; + +export const BOTTOM_BAR_HEIGHT = 25; + +interface EditorBottomBarProps { + width: number | string; +} + +function EditorBottomBar(props: EditorBottomBarProps) { + const { width } = props; + const dispatch = useDispatch(); + const configStore = useSelector(selectConfig); + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const handleChangeCodeKey = (newKeyCode: CodeKeyType) => { + dispatch(setCodeKeyType(newKeyCode)); + handleClose(); + }; + + return ( + + + + {Object.values(CodeKeyType).map((keyType) => ( + handleChangeCodeKey(keyType)}> + {keyType} + + ))} + + + ); +} + +export default EditorBottomBar; diff --git a/frontend/src/components/editor/Preview.tsx b/frontend/src/components/editor/Preview.tsx index 6e1d493b..c9d7a3bc 100644 --- a/frontend/src/components/editor/Preview.tsx +++ b/frontend/src/components/editor/Preview.tsx @@ -6,8 +6,8 @@ import { useEffect, useState } from "react"; import { useSelector } from "react-redux"; import rehypeExternalLinks from "rehype-external-links"; import rehypeKatex from "rehype-katex"; -import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; import { getCodeString } from "rehype-rewrite"; +import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; import remarkMath from "remark-math"; import { useCurrentTheme } from "../../hooks/useCurrentTheme"; import { selectEditor } from "../../store/editorSlice"; diff --git a/frontend/src/components/headers/DocumentHeader.tsx b/frontend/src/components/headers/DocumentHeader.tsx index 75f014b4..89e86769 100644 --- a/frontend/src/components/headers/DocumentHeader.tsx +++ b/frontend/src/components/headers/DocumentHeader.tsx @@ -18,6 +18,7 @@ import { useNavigate } from "react-router-dom"; import { useUserPresence } from "../../hooks/useUserPresence"; import { EditorModeType, selectEditor, setMode } from "../../store/editorSlice"; import { selectWorkspace } from "../../store/workspaceSlice"; +import { ShareRole } from "../../utils/share"; import DownloadMenu from "../common/DownloadMenu"; import ShareButton from "../common/ShareButton"; import ThemeButton from "../common/ThemeButton"; @@ -31,8 +32,8 @@ function DocumentHeader() { const { presenceList } = useUserPresence(editorState.doc); useEffect(() => { - if (editorState.shareRole === "READ") { - dispatch(setMode("read")); + if (editorState.shareRole === ShareRole.READ) { + dispatch(setMode(EditorModeType.READ)); } }, [dispatch, editorState.shareRole]); @@ -58,7 +59,7 @@ function DocumentHeader() { )} - {editorState.shareRole !== "READ" && ( + {editorState.shareRole !== ShareRole.READ && ( { - dispatch(setTheme(themeMode == "light" ? "dark" : "light")); + dispatch(setTheme(themeMode == ThemeType.LIGHT ? ThemeType.DARK : ThemeType.LIGHT)); }; return ( diff --git a/frontend/src/pages/workspace/document/share/Index.tsx b/frontend/src/pages/workspace/document/share/Index.tsx index 365da099..17132416 100644 --- a/frontend/src/pages/workspace/document/share/Index.tsx +++ b/frontend/src/pages/workspace/document/share/Index.tsx @@ -5,8 +5,15 @@ import { Navigate, useLocation, useSearchParams } from "react-router-dom"; import DocumentView from "../../../../components/editor/DocumentView"; import { useGetDocumentBySharingTokenQuery } from "../../../../hooks/api/document"; import { useYorkieDocument } from "../../../../hooks/useYorkieDocument"; -import { setClient, setDoc, setMode, setShareRole } from "../../../../store/editorSlice"; +import { + EditorModeType, + setClient, + setDoc, + setMode, + setShareRole, +} from "../../../../store/editorSlice"; import { selectUser } from "../../../../store/userSlice"; +import { ShareRole } from "../../../../utils/share"; function DocumentShareIndex() { const dispatch = useDispatch(); @@ -25,8 +32,8 @@ function DocumentShareIndex() { dispatch(setShareRole(sharedDocument.role)); - if (sharedDocument.role === "READ") { - dispatch(setMode("read")); + if (sharedDocument.role === ShareRole.READ) { + dispatch(setMode(EditorModeType.READ)); } }, [dispatch, sharedDocument?.role]); diff --git a/frontend/src/store/configSlice.ts b/frontend/src/store/configSlice.ts index a7eca082..f4f40a09 100644 --- a/frontend/src/store/configSlice.ts +++ b/frontend/src/store/configSlice.ts @@ -2,20 +2,31 @@ import { createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "./store"; -type ThemeType = "default" | "dark" | "light"; +export enum ThemeType { + DEFAULT = "default", + DARK = "dark", + LIGHT = "light", +} + +export enum CodeKeyType { + SUBLIME = "sublime", + VIM = "vim", +} export interface ConfigState { theme: ThemeType; drawerOpen: boolean; + codeKey: CodeKeyType; } const initialState: ConfigState = { - theme: "default", + theme: ThemeType.DEFAULT, drawerOpen: true, + codeKey: CodeKeyType.SUBLIME, }; export const configSlice = createSlice({ - name: "editor", + name: "config", initialState, reducers: { setTheme: (state, action: PayloadAction) => { @@ -24,10 +35,13 @@ export const configSlice = createSlice({ setDrawerOpen: (state, action: PayloadAction) => { state.drawerOpen = action.payload; }, + setCodeKeyType: (state, action: PayloadAction) => { + state.codeKey = action.payload; + }, }, }); -export const { setTheme, setDrawerOpen } = configSlice.actions; +export const { setTheme, setDrawerOpen, setCodeKeyType } = configSlice.actions; export const selectConfig = (state: RootState) => state.config; diff --git a/frontend/src/store/editorSlice.ts b/frontend/src/store/editorSlice.ts index c6d7ee4f..b4652f32 100644 --- a/frontend/src/store/editorSlice.ts +++ b/frontend/src/store/editorSlice.ts @@ -1,12 +1,17 @@ -import { createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; -import { RootState } from "./store"; +import { createSlice } from "@reduxjs/toolkit"; +import { EditorView } from "codemirror"; import * as yorkie from "yorkie-js-sdk"; -import { YorkieCodeMirrorDocType, YorkieCodeMirrorPresenceType } from "../utils/yorkie/yorkieSync"; import { ShareRole } from "../utils/share"; -import { EditorView } from "codemirror"; +import { YorkieCodeMirrorDocType, YorkieCodeMirrorPresenceType } from "../utils/yorkie/yorkieSync"; +import { RootState } from "./store"; + +export enum EditorModeType { + EDIT = "edit", + BOTH = "both", + READ = "read", +} -export type EditorModeType = "edit" | "both" | "read"; export type CodePairDocType = yorkie.Document< YorkieCodeMirrorDocType, YorkieCodeMirrorPresenceType @@ -21,7 +26,7 @@ export interface EditorState { } const initialState: EditorState = { - mode: "both", + mode: EditorModeType.BOTH, shareRole: null, doc: null, client: null, @@ -50,7 +55,7 @@ export const editorSlice = createSlice({ }, }); -export const { setMode, setDoc, setClient, setShareRole, setCmView } = editorSlice.actions; +export const { setMode, setShareRole, setDoc, setClient, setCmView } = editorSlice.actions; export const selectEditor = (state: RootState) => state.editor; From 5d5ed73f00dfe39e31194a89113a7b20e68e217d Mon Sep 17 00:00:00 2001 From: XETA Date: Wed, 11 Sep 2024 23:45:39 +0900 Subject: [PATCH 13/20] Implement Refresh Token (#317) * Implement refresh token in backend * Implement token refresh with axios interceptor * Fix typo * Apply lint * Add Refresh token initialization * Update Axios interceptor to handle token expiration and refresh logic * Update environment variable comments for clarity * Separate dtos related to refresh token * Add `@ApiProperty` decorator * Update `@ApiBody` and `@ApiResponse` type * Separate JWT secrets for access and refresh tokens * Add logout logic and remove duplicate call * Update env variable comments for clarity * Split JwtService for access and refresh tokens * Fix interceptor logic for refresh token * Add error config token --- backend/.env.development | 11 +++- backend/src/auth/auth.controller.ts | 49 ++++++++++------ backend/src/auth/auth.module.ts | 46 ++++++++++----- backend/src/auth/auth.service.spec.ts | 48 +++++++++++++++- backend/src/auth/auth.service.ts | 36 +++++++++++- .../src/auth/dto/refresh-token-request.dto.ts | 6 ++ .../auth/dto/refresh-token-response.dto.ts | 6 ++ backend/src/auth/jwt-refresh.strategy.ts | 26 +++++++++ backend/src/auth/jwt.strategy.ts | 8 +-- backend/src/auth/types/login-response.type.ts | 3 + backend/src/users/users.module.ts | 4 +- backend/src/utils/constants/jwt-inject.ts | 4 ++ frontend/src/App.tsx | 57 +++++++++++++++---- .../components/popovers/ProfilePopover.tsx | 4 +- frontend/src/hooks/api/user.ts | 8 +-- frontend/src/pages/auth/callback/Index.tsx | 10 ++-- frontend/src/store/authSlice.ts | 15 ++++- 17 files changed, 278 insertions(+), 63 deletions(-) create mode 100644 backend/src/auth/dto/refresh-token-request.dto.ts create mode 100644 backend/src/auth/dto/refresh-token-response.dto.ts create mode 100644 backend/src/auth/jwt-refresh.strategy.ts create mode 100644 backend/src/utils/constants/jwt-inject.ts diff --git a/backend/.env.development b/backend/.env.development index 2df84ab8..0ecfa7ba 100644 --- a/backend/.env.development +++ b/backend/.env.development @@ -14,9 +14,14 @@ GITHUB_CLIENT_SECRET=your_github_client_secret_here # Example: http://localhost:3000/auth/login/github (For development mode) GITHUB_CALLBACK_URL=http://localhost:3000/auth/login/github -# JWT_AUTH_SECRET: Secret key for JWT authentication. -# This key is used to sign and verify JWT tokens. -JWT_AUTH_SECRET=you_should_change_this_secret_key_in_production +# JWT_ACCESS_TOKEN_SECRET: Secret key for signing and verifying access tokens. +# JWT_ACCESS_TOKEN_EXPIRATION_TIME: Expiration time for access tokens in seconds. +JWT_ACCESS_TOKEN_SECRET=you_should_change_this_access_token_secret_key_in_production +JWT_ACCESS_TOKEN_EXPIRATION_TIME=86400 +# JWT_REFRESH_TOKEN_SECRET: Secret key for signing and verifying refresh tokens. +# JWT_REFRESH_TOKEN_EXPIRATION_TIME: Expiration time for refresh tokens in seconds. +JWT_REFRESH_TOKEN_SECRET=you_should_change_this_refresh_token_secret_key_in_production +JWT_REFRESH_TOKEN_EXPIRATION_TIME=604800 # FRONTEND_BASE_URL: Base URL of the frontend application. # This URL is used for redirecting after authentication, etc. diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index 9bfc0215..257da929 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -1,20 +1,29 @@ -import { Controller, Get, HttpRedirectResponse, Redirect, Req, UseGuards } from "@nestjs/common"; +import { + Body, + Controller, + Get, + HttpRedirectResponse, + Post, + Redirect, + Req, + UseGuards, +} from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { AuthGuard } from "@nestjs/passport"; +import { ApiBody, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { Public } from "src/utils/decorators/auth.decorator"; +import { AuthService } from "./auth.service"; +import { RefreshTokenRequestDto } from "./dto/refresh-token-request.dto"; +import { RefreshTokenResponseDto } from "./dto/refresh-token-response.dto"; import { LoginRequest } from "./types/login-request.type"; -import { JwtService } from "@nestjs/jwt"; import { LoginResponse } from "./types/login-response.type"; -import { UsersService } from "src/users/users.service"; -import { Public } from "src/utils/decorators/auth.decorator"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ConfigService } from "@nestjs/config"; @ApiTags("Auth") @Controller("auth") export class AuthController { constructor( - private configService: ConfigService, - private jwtService: JwtService, - private usersService: UsersService + private readonly authService: AuthService, + private configService: ConfigService ) {} @Public() @@ -28,16 +37,24 @@ export class AuthController { }) @ApiResponse({ type: LoginResponse }) async login(@Req() req: LoginRequest): Promise { - const user = await this.usersService.findOrCreate( - req.user.socialProvider, - req.user.socialUid - ); - - const accessToken = this.jwtService.sign({ sub: user.id, nickname: user.nickname }); + const { accessToken, refreshToken } = await this.authService.loginWithSocialProvider(req); return { - url: `${this.configService.get("FRONTEND_BASE_URL")}/auth/callback?token=${accessToken}`, + url: `${this.configService.get("FRONTEND_BASE_URL")}/auth/callback?accessToken=${accessToken}&refreshToken=${refreshToken}`, statusCode: 302, }; } + + @Public() + @Post("refresh") + @UseGuards(AuthGuard("refresh")) + @ApiOperation({ + summary: "Refresh Access Token", + description: "Generates a new Access Token using the user's Refresh Token.", + }) + @ApiBody({ type: RefreshTokenRequestDto }) + @ApiResponse({ type: RefreshTokenResponseDto }) + async refresh(@Body() body: RefreshTokenRequestDto): Promise { + return await this.authService.getNewAccessToken(body.refreshToken); + } } diff --git a/backend/src/auth/auth.module.ts b/backend/src/auth/auth.module.ts index a7902981..68850b98 100644 --- a/backend/src/auth/auth.module.ts +++ b/backend/src/auth/auth.module.ts @@ -1,27 +1,47 @@ import { Module } from "@nestjs/common"; -import { AuthService } from "./auth.service"; +import { ConfigService } from "@nestjs/config"; +import { JwtService } from "@nestjs/jwt"; import { UsersModule } from "src/users/users.module"; +import { JwtInject } from "src/utils/constants/jwt-inject"; import { AuthController } from "./auth.controller"; +import { AuthService } from "./auth.service"; import { GithubStrategy } from "./github.strategy"; -import { ConfigService } from "@nestjs/config"; -import { JwtModule } from "@nestjs/jwt"; +import { JwtRefreshStrategy } from "./jwt-refresh.strategy"; import { JwtStrategy } from "./jwt.strategy"; @Module({ - imports: [ - UsersModule, - JwtModule.registerAsync({ + imports: [UsersModule], + providers: [ + AuthService, + GithubStrategy, + JwtStrategy, + JwtRefreshStrategy, + { + provide: JwtInject.ACCESS, + useFactory: async (configService: ConfigService) => { + return new JwtService({ + secret: configService.get("JWT_ACCESS_TOKEN_SECRET"), + signOptions: { + expiresIn: `${configService.get("JWT_ACCESS_TOKEN_EXPIRATION_TIME")}s`, + }, + }); + }, + inject: [ConfigService], + }, + { + provide: JwtInject.REFRESH, useFactory: async (configService: ConfigService) => { - return { - global: true, - signOptions: { expiresIn: "24h" }, - secret: configService.get("JWT_AUTH_SECRET"), - }; + return new JwtService({ + secret: configService.get("JWT_REFRESH_TOKEN_SECRET"), + signOptions: { + expiresIn: `${configService.get("JWT_REFRESH_TOKEN_EXPIRATION_TIME")}s`, + }, + }); }, inject: [ConfigService], - }), + }, ], - providers: [AuthService, GithubStrategy, JwtStrategy], + exports: [JwtInject.ACCESS, JwtInject.REFRESH], controllers: [AuthController], }) export class AuthModule {} diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts index 5430748f..53df34b3 100644 --- a/backend/src/auth/auth.service.spec.ts +++ b/backend/src/auth/auth.service.spec.ts @@ -1,18 +1,64 @@ +import { ConfigModule } from "@nestjs/config"; +import { JwtService } from "@nestjs/jwt"; import { Test, TestingModule } from "@nestjs/testing"; +import { UsersService } from "../users/users.service"; import { AuthService } from "./auth.service"; describe("AuthService", () => { let service: AuthService; + let jwtService: JwtService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [AuthService], + imports: [ConfigModule.forRoot()], + providers: [ + AuthService, + { + provide: UsersService, + useValue: { + findOrCreate: jest + .fn() + .mockResolvedValue({ id: "123", nickname: "testuser" }), + }, + }, + { + provide: JwtService, + useValue: { + sign: jest.fn().mockReturnValue("signedToken"), + verify: jest.fn().mockReturnValue({ sub: "123", nickname: "testuser" }), + }, + }, + ], }).compile(); service = module.get(AuthService); + jwtService = module.get(JwtService); }); it("should be defined", () => { expect(service).toBeDefined(); }); + + describe("getNewAccessToken", () => { + it("should generate a new access token using refresh token", async () => { + const newToken = await service.getNewAccessToken("refreshToken"); + + expect(newToken).toBe("signedToken"); + expect(jwtService.verify).toHaveBeenCalledWith("refreshToken"); + expect(jwtService.sign).toHaveBeenCalledWith( + { sub: "123", nickname: "testuser" }, + expect.any(Object) + ); + }); + + it("should throw an error if refresh token is invalid", async () => { + jwtService.verify = jest.fn().mockImplementation(() => { + throw new Error("Invalid token"); + }); + + await expect(service.getNewAccessToken("invalidToken")).rejects.toThrow( + "Invalid token" + ); + }); + }); }); diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index beddfe5b..9f8dcc76 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -1,7 +1,39 @@ -import { Injectable } from "@nestjs/common"; +import { Inject, Injectable } from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; import { UsersService } from "src/users/users.service"; +import { JwtInject } from "src/utils/constants/jwt-inject"; +import { RefreshTokenResponseDto } from "./dto/refresh-token-response.dto"; +import { LoginRequest } from "./types/login-request.type"; +import { LoginResponse } from "./types/login-response.type"; @Injectable() export class AuthService { - constructor(private usersService: UsersService) {} + constructor( + private readonly usersService: UsersService, + @Inject(JwtInject.ACCESS) private readonly jwtAccessService: JwtService, + @Inject(JwtInject.REFRESH) private readonly jwtRefreshService: JwtService + ) {} + + async loginWithSocialProvider(req: LoginRequest): Promise { + const user = await this.usersService.findOrCreate( + req.user.socialProvider, + req.user.socialUid + ); + + const accessToken = this.jwtAccessService.sign({ sub: user.id, nickname: user.nickname }); + const refreshToken = this.jwtRefreshService.sign({ sub: user.id }); + + return { accessToken, refreshToken }; + } + + async getNewAccessToken(refreshToken: string): Promise { + const payload = this.jwtRefreshService.verify(refreshToken); + + const newAccessToken = this.jwtAccessService.sign({ + sub: payload.sub, + nickname: payload.nickname, + }); + + return { newAccessToken }; + } } diff --git a/backend/src/auth/dto/refresh-token-request.dto.ts b/backend/src/auth/dto/refresh-token-request.dto.ts new file mode 100644 index 00000000..72fe7db5 --- /dev/null +++ b/backend/src/auth/dto/refresh-token-request.dto.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class RefreshTokenRequestDto { + @ApiProperty({ type: String, description: "The refresh token to request a new access token" }) + refreshToken: string; +} diff --git a/backend/src/auth/dto/refresh-token-response.dto.ts b/backend/src/auth/dto/refresh-token-response.dto.ts new file mode 100644 index 00000000..0dbfb96d --- /dev/null +++ b/backend/src/auth/dto/refresh-token-response.dto.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class RefreshTokenResponseDto { + @ApiProperty({ type: String, description: "The new access token" }) + newAccessToken: string; +} diff --git a/backend/src/auth/jwt-refresh.strategy.ts b/backend/src/auth/jwt-refresh.strategy.ts new file mode 100644 index 00000000..13840b56 --- /dev/null +++ b/backend/src/auth/jwt-refresh.strategy.ts @@ -0,0 +1,26 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PassportStrategy } from "@nestjs/passport"; +import { Strategy as PassportJwtStrategy } from "passport-jwt"; +import { JwtPayload } from "src/utils/types/jwt.type"; +import { AuthorizedUser } from "src/utils/types/req.type"; + +@Injectable() +export class JwtRefreshStrategy extends PassportStrategy(PassportJwtStrategy, "refresh") { + constructor(configService: ConfigService) { + super({ + jwtFromRequest: (req) => { + if (req && req.body.refreshToken) { + return req.body.refreshToken; + } + return null; + }, + ignoreExpiration: false, + secretOrKey: configService.get("JWT_REFRESH_TOKEN_SECRET"), + }); + } + + async validate(payload: JwtPayload): Promise { + return { id: payload.sub, nickname: payload.nickname }; + } +} diff --git a/backend/src/auth/jwt.strategy.ts b/backend/src/auth/jwt.strategy.ts index 7c823730..54b87a29 100644 --- a/backend/src/auth/jwt.strategy.ts +++ b/backend/src/auth/jwt.strategy.ts @@ -1,17 +1,17 @@ -import { ExtractJwt, Strategy as PassportJwtStrategy } from "passport-jwt"; -import { ConfigService } from "@nestjs/config"; import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { PassportStrategy } from "@nestjs/passport"; +import { ExtractJwt, Strategy as PassportJwtStrategy } from "passport-jwt"; import { JwtPayload } from "src/utils/types/jwt.type"; import { AuthorizedUser } from "src/utils/types/req.type"; @Injectable() -export class JwtStrategy extends PassportStrategy(PassportJwtStrategy) { +export class JwtStrategy extends PassportStrategy(PassportJwtStrategy, "jwt") { constructor(configService: ConfigService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, - secretOrKey: configService.get("JWT_AUTH_SECRET"), + secretOrKey: configService.get("JWT_ACCESS_TOKEN_SECRET"), }); } diff --git a/backend/src/auth/types/login-response.type.ts b/backend/src/auth/types/login-response.type.ts index 99542f23..991f2e59 100644 --- a/backend/src/auth/types/login-response.type.ts +++ b/backend/src/auth/types/login-response.type.ts @@ -3,4 +3,7 @@ import { ApiProperty } from "@nestjs/swagger"; export class LoginResponse { @ApiProperty({ type: String, description: "Access token for CodePair" }) accessToken: string; + + @ApiProperty({ type: String, description: "Refresh token to get a new access token" }) + refreshToken: string; } diff --git a/backend/src/users/users.module.ts b/backend/src/users/users.module.ts index 7930a041..858a5073 100644 --- a/backend/src/users/users.module.ts +++ b/backend/src/users/users.module.ts @@ -1,8 +1,8 @@ import { Module } from "@nestjs/common"; -import { UsersService } from "./users.service"; +import { CheckService } from "src/check/check.service"; import { PrismaService } from "src/db/prisma.service"; import { UsersController } from "./users.controller"; -import { CheckService } from "src/check/check.service"; +import { UsersService } from "./users.service"; @Module({ providers: [UsersService, PrismaService, CheckService], diff --git a/backend/src/utils/constants/jwt-inject.ts b/backend/src/utils/constants/jwt-inject.ts new file mode 100644 index 00000000..b2734970 --- /dev/null +++ b/backend/src/utils/constants/jwt-inject.ts @@ -0,0 +1,4 @@ +export const JwtInject = { + ACCESS: "JWT_ACCESS_SERVICE", + REFRESH: "JWT_REFRESH_SERVICE", +}; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9e99e260..fd0b543e 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,9 +2,12 @@ import "@fontsource/roboto/300.css"; import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; import "@fontsource/roboto/700.css"; -import "./App.css"; import { Box, CssBaseline, ThemeProvider, createTheme, useMediaQuery } from "@mui/material"; -import { useSelector } from "react-redux"; +import * as Sentry from "@sentry/react"; +import { QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import axios from "axios"; +import { useEffect, useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { RouterProvider, createBrowserRouter, @@ -13,15 +16,15 @@ import { useLocation, useNavigationType, } from "react-router-dom"; -import { useEffect, useMemo } from "react"; -import { selectConfig } from "./store/configSlice"; -import axios from "axios"; -import { routes } from "./routes"; -import { QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import AuthProvider from "./providers/AuthProvider"; -import { useErrorHandler } from "./hooks/useErrorHandler"; -import * as Sentry from "@sentry/react"; +import "./App.css"; import { useGetSettingsQuery } from "./hooks/api/settings"; +import { useErrorHandler } from "./hooks/useErrorHandler"; +import AuthProvider from "./providers/AuthProvider"; +import { routes } from "./routes"; +import { logout, setAccessToken } from "./store/authSlice"; +import { selectConfig } from "./store/configSlice"; +import { store } from "./store/store"; +import { setUserData } from "./store/userSlice"; import { isAxios404Error, isAxios500Error } from "./utils/axios.default"; if (import.meta.env.PROD) { @@ -58,6 +61,7 @@ function SettingLoader() { function App() { const config = useSelector(selectConfig); + const dispatch = useDispatch(); const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); const theme = useMemo(() => { const defaultMode = prefersDarkMode ? "dark" : "light"; @@ -104,6 +108,39 @@ function App() { }); }, [handleError]); + useEffect(() => { + const handleRefreshTokenExpiration = () => { + dispatch(logout()); + dispatch(setUserData(null)); + }; + + const interceptor = axios.interceptors.response.use( + (response) => response, + async (error) => { + if (error.response?.status === 401 && !error.config._retry) { + if (error.config.url === "/auth/refresh") { + handleRefreshTokenExpiration(); + return Promise.reject(error); + } else { + error.config._retry = true; + const refreshToken = store.getState().auth.refreshToken; + const response = await axios.post("/auth/refresh", { refreshToken }); + const newAccessToken = response.data.newAccessToken; + dispatch(setAccessToken(newAccessToken)); + axios.defaults.headers.common["Authorization"] = `Bearer ${newAccessToken}`; + error.config.headers["Authorization"] = `Bearer ${newAccessToken}`; + return axios(error.config); + } + } + return Promise.reject(error); + } + ); + + return () => { + axios.interceptors.response.eject(interceptor); + }; + }, [dispatch]); + return ( diff --git a/frontend/src/components/popovers/ProfilePopover.tsx b/frontend/src/components/popovers/ProfilePopover.tsx index 27fd5f80..566f7981 100644 --- a/frontend/src/components/popovers/ProfilePopover.tsx +++ b/frontend/src/components/popovers/ProfilePopover.tsx @@ -13,7 +13,7 @@ import { import { useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; import { useCurrentTheme } from "../../hooks/useCurrentTheme"; -import { setAccessToken } from "../../store/authSlice"; +import { logout } from "../../store/authSlice"; import { setTheme, ThemeType } from "../../store/configSlice"; import { setUserData } from "../../store/userSlice"; @@ -23,7 +23,7 @@ function ProfilePopover(props: PopoverProps) { const navigate = useNavigate(); const handleLogout = () => { - dispatch(setAccessToken(null)); + dispatch(logout()); dispatch(setUserData(null)); }; diff --git a/frontend/src/hooks/api/user.ts b/frontend/src/hooks/api/user.ts index cf673868..992f9838 100644 --- a/frontend/src/hooks/api/user.ts +++ b/frontend/src/hooks/api/user.ts @@ -1,10 +1,10 @@ -import { useDispatch, useSelector } from "react-redux"; -import { selectAuth, setAccessToken } from "../../store/authSlice"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import axios from "axios"; -import { GetUserResponse, UpdateUserRequest } from "./types/user"; import { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { logout, selectAuth } from "../../store/authSlice"; import { User, setUserData } from "../../store/userSlice"; +import { GetUserResponse, UpdateUserRequest } from "./types/user"; export const generateGetUserQueryKey = (accessToken: string) => { return ["users", accessToken]; @@ -28,7 +28,7 @@ export const useGetUserQuery = () => { if (query.isSuccess) { dispatch(setUserData(query.data as User)); } else if (query.isError) { - dispatch(setAccessToken(null)); + dispatch(logout()); dispatch(setUserData(null)); axios.defaults.headers.common["Authorization"] = ""; } diff --git a/frontend/src/pages/auth/callback/Index.tsx b/frontend/src/pages/auth/callback/Index.tsx index 77d363e2..1f7dd193 100644 --- a/frontend/src/pages/auth/callback/Index.tsx +++ b/frontend/src/pages/auth/callback/Index.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; import { useEffect } from "react"; import { useDispatch } from "react-redux"; import { useNavigate, useSearchParams } from "react-router-dom"; -import { setAccessToken } from "../../../store/authSlice"; +import { setAccessToken, setRefreshToken } from "../../../store/authSlice"; function CallbackIndex() { const dispatch = useDispatch(); @@ -10,14 +10,16 @@ function CallbackIndex() { const [searchParams] = useSearchParams(); useEffect(() => { - const token = searchParams.get("token"); + const accessToken = searchParams.get("accessToken"); + const refreshToken = searchParams.get("refreshToken"); - if (!token) { + if (!accessToken || !refreshToken) { navigate("/"); return; } - dispatch(setAccessToken(token)); + dispatch(setAccessToken(accessToken)); + dispatch(setRefreshToken(refreshToken)); }, [dispatch, navigate, searchParams]); return ; diff --git a/frontend/src/store/authSlice.ts b/frontend/src/store/authSlice.ts index 4ecf3371..aefa66f2 100644 --- a/frontend/src/store/authSlice.ts +++ b/frontend/src/store/authSlice.ts @@ -1,13 +1,16 @@ -import { createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; +import { createSlice } from "@reduxjs/toolkit"; +import axios from "axios"; import { RootState } from "./store"; export interface AuthState { accessToken: string | null; + refreshToken: string | null; } const initialState: AuthState = { accessToken: null, + refreshToken: null, }; export const authSlice = createSlice({ @@ -17,10 +20,18 @@ export const authSlice = createSlice({ setAccessToken: (state, action: PayloadAction) => { state.accessToken = action.payload; }, + setRefreshToken(state, action: PayloadAction) { + state.refreshToken = action.payload; + }, + logout: (state) => { + state.accessToken = null; + state.refreshToken = null; + axios.defaults.headers.common["Authorization"] = ""; + }, }, }); -export const { setAccessToken } = authSlice.actions; +export const { setAccessToken, setRefreshToken, logout } = authSlice.actions; export const selectAuth = (state: RootState) => state.auth; From c7f36008ee612d5cbe8d11193d1bc961102df91a Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:00:53 +0900 Subject: [PATCH 14/20] Update `CHANGELOG.md` for `v0.1.11` (#343) --- CHANGELOG.md | 11 +++++++++++ backend/package-lock.json | 4 ++-- backend/package.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dd7c535..6d3bf426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and CodePair adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [0.1.11] - 20204-09-12 + +### Added + +- Add Vim binding support for CodePair using CodeMirror 6 by @choidabom in https://github.com/yorkie-team/codepair/pull/340 +- Implement Refresh Token by @xet-a in https://github.com/yorkie-team/codepair/pull/317 + +### Changed + +- Remove unnecessary `LANGCHAIN_ENDPOINT` environment variable due to LangSmith API Key update by @devleejb in https://github.com/yorkie-team/codepair/pull/336 + ## [0.1.10] - 20204-09-05 ### Added diff --git a/backend/package-lock.json b/backend/package-lock.json index 1b130271..bbb81638 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-backend", - "version": "0.1.10", + "version": "0.1.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-backend", - "version": "0.1.10", + "version": "0.1.11", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-s3": "^3.509.0", diff --git a/backend/package.json b/backend/package.json index 5a021e43..7cb2867d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "codepair-backend", - "version": "0.1.10", + "version": "0.1.11", "description": "CodePair Backend", "author": "yorkie-team", "private": true, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d853b4fb..6aff1af9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-frontend", - "version": "0.1.10", + "version": "0.1.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-frontend", - "version": "0.1.10", + "version": "0.1.11", "license": "Apache-2.0", "dependencies": { "@codemirror/commands": "6.1.2", diff --git a/frontend/package.json b/frontend/package.json index cdcd38dd..2943c1e7 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,7 +2,7 @@ "name": "codepair-frontend", "description": "CodePair Frontend", "private": true, - "version": "0.1.10", + "version": "0.1.11", "type": "module", "author": "yorkie-team", "license": "Apache-2.0", From 34a52c33914cd8b996519dbb7c008c2c7c76f23b Mon Sep 17 00:00:00 2001 From: choidabom <48302257+choidabom@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:26:39 +0900 Subject: [PATCH 15/20] Add missing refresh token settings in `docker-compose-full.yml` (#344) --- backend/docker/docker-compose-full.yml | 5 ++++- backend/src/check/check.module.ts | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/docker/docker-compose-full.yml b/backend/docker/docker-compose-full.yml index 621534cd..fa0a23df 100644 --- a/backend/docker/docker-compose-full.yml +++ b/backend/docker/docker-compose-full.yml @@ -11,7 +11,10 @@ services: GITHUB_CLIENT_ID: "your_github_client_id_here" GITHUB_CLIENT_SECRET: "your_github_client_secret_here" GITHUB_CALLBACK_URL: "http://localhost:3000/auth/login/github" - JWT_AUTH_SECRET: "you_should_change_this_secret_key_in_production" + JWT_ACCESS_TOKEN_SECRET: "you_should_change_this_access_token_secret_key_in_production" + JWT_ACCESS_TOKEN_EXPIRATION_TIME: 86400 + JWT_REFRESH_TOKEN_SECRET: "you_should_change_this_refresh_token_secret_key_in_production" + JWT_REFRESH_TOKEN_EXPIRATION_TIME: 604800 FRONTEND_BASE_URL: "http://localhost:5173" YORKIE_API_ADDR: "http://yorkie:8080" YORKIE_PROJECT_NAME: "default" diff --git a/backend/src/check/check.module.ts b/backend/src/check/check.module.ts index 653438c6..5016ab7c 100644 --- a/backend/src/check/check.module.ts +++ b/backend/src/check/check.module.ts @@ -11,8 +11,10 @@ import { JwtModule } from "@nestjs/jwt"; global: true, useFactory: async (configService: ConfigService) => { return { - signOptions: { expiresIn: "24h" }, - secret: configService.get("JWT_AUTH_SECRET"), + secret: configService.get("JWT_ACCESS_TOKEN_SECRET"), + signOptions: { + expiresIn: `${configService.get("JWT_ACCESS_TOKEN_EXPIRATION_TIME")}s`, + }, }; }, inject: [ConfigService], From 5d681e0ef2ceff55b9679e53d0a29695e51ac8c9 Mon Sep 17 00:00:00 2001 From: choidabom <48302257+choidabom@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:50:27 +0900 Subject: [PATCH 16/20] Fix duplicate text selection issue (#345) --- frontend/package-lock.json | 27 +++++++++++++++++++++++ frontend/package.json | 1 + frontend/src/components/editor/Editor.tsx | 5 +++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6aff1af9..fa89d88f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,6 +24,7 @@ "@sentry/react": "^7.99.0", "@swc/helpers": "^0.5.3", "@tanstack/react-query": "^5.17.15", + "@uiw/codemirror-extensions-basic-setup": "^4.23.2", "@uiw/codemirror-theme-xcode": "^4.21.21", "@uiw/codemirror-themes": "^4.21.21", "@uiw/react-markdown-preview": "^5.0.7", @@ -3062,6 +3063,32 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/codemirror-extensions-basic-setup": { + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.2.tgz", + "integrity": "sha512-eacivkj7wzskl2HBYs4rfN0CbYlsSQh5ADtOYWTpc8Txm4ONw8RTi4/rxF6Ks2vdaovizewU5QaHximbxoNTrw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/autocomplete": ">=6.0.0", + "@codemirror/commands": ">=6.0.0", + "@codemirror/language": ">=6.0.0", + "@codemirror/lint": ">=6.0.0", + "@codemirror/search": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, "node_modules/@uiw/codemirror-theme-xcode": { "version": "4.23.0", "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-xcode/-/codemirror-theme-xcode-4.23.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2943c1e7..84630660 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,7 @@ "@sentry/react": "^7.99.0", "@swc/helpers": "^0.5.3", "@tanstack/react-query": "^5.17.15", + "@uiw/codemirror-extensions-basic-setup": "^4.23.2", "@uiw/codemirror-theme-xcode": "^4.21.21", "@uiw/codemirror-themes": "^4.21.21", "@uiw/react-markdown-preview": "^5.0.7", diff --git a/frontend/src/components/editor/Editor.tsx b/frontend/src/components/editor/Editor.tsx index 1c6dfe29..64843a66 100644 --- a/frontend/src/components/editor/Editor.tsx +++ b/frontend/src/components/editor/Editor.tsx @@ -2,8 +2,9 @@ import { markdown } from "@codemirror/lang-markdown"; import { EditorState } from "@codemirror/state"; import { keymap } from "@codemirror/view"; import { Vim, vim } from "@replit/codemirror-vim"; +import { basicSetup } from "@uiw/codemirror-extensions-basic-setup"; import { xcodeDark, xcodeLight } from "@uiw/codemirror-theme-xcode"; -import { basicSetup, EditorView } from "codemirror"; +import { EditorView } from "codemirror"; import { useCallback, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { ScrollSyncPane } from "react-scroll-sync"; @@ -73,7 +74,7 @@ function Editor(props: EditorProps) { doc: editorStore.doc.getRoot().content?.toString() ?? "", extensions: [ keymap.of(setKeymapConfig()), - basicSetup, + basicSetup({ highlightSelectionMatches: false }), markdown(), configStore.codeKey === CodeKeyType.VIM ? vim() : [], themeMode == "light" ? xcodeLight : xcodeDark, From c75c6af440b5857c4f3ae5ef6ec1920727b0c54a Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:51:57 +0900 Subject: [PATCH 17/20] Change the order of vim key binding (#346) --- frontend/src/components/editor/Editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/editor/Editor.tsx b/frontend/src/components/editor/Editor.tsx index 64843a66..8c159ca3 100644 --- a/frontend/src/components/editor/Editor.tsx +++ b/frontend/src/components/editor/Editor.tsx @@ -73,10 +73,10 @@ function Editor(props: EditorProps) { const state = EditorState.create({ doc: editorStore.doc.getRoot().content?.toString() ?? "", extensions: [ + configStore.codeKey === CodeKeyType.VIM ? vim() : [], keymap.of(setKeymapConfig()), basicSetup({ highlightSelectionMatches: false }), markdown(), - configStore.codeKey === CodeKeyType.VIM ? vim() : [], themeMode == "light" ? xcodeLight : xcodeDark, EditorView.theme({ "&": { width: "100%" } }), EditorView.lineWrapping, From 03635fa8d16c0f3c211efb1effb575adb6451234 Mon Sep 17 00:00:00 2001 From: Youngteac Hong Date: Fri, 13 Sep 2024 16:10:01 +0900 Subject: [PATCH 18/20] Fix issue where `jj` could not be entered in Vim mode (#347) * Fix issue where `jj` could not be entered in Vim mode * Update Editor.tsx --- frontend/src/components/editor/Editor.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/components/editor/Editor.tsx b/frontend/src/components/editor/Editor.tsx index 8c159ca3..a7b5fd1e 100644 --- a/frontend/src/components/editor/Editor.tsx +++ b/frontend/src/components/editor/Editor.tsx @@ -1,7 +1,7 @@ import { markdown } from "@codemirror/lang-markdown"; import { EditorState } from "@codemirror/state"; import { keymap } from "@codemirror/view"; -import { Vim, vim } from "@replit/codemirror-vim"; +import { vim } from "@replit/codemirror-vim"; import { basicSetup } from "@uiw/codemirror-extensions-basic-setup"; import { xcodeDark, xcodeLight } from "@uiw/codemirror-theme-xcode"; import { EditorView } from "codemirror"; @@ -94,9 +94,6 @@ function Editor(props: EditorProps) { ], }); - // Vim key mapping: Map 'jj' to '' in insert mode - Vim.map("jj", "", "insert"); - const view = new EditorView({ state, parent: element }); dispatch(setCmView(view)); From 2e438c9ab0ad09bf40ce3cb74364442285b51fec Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:33:57 +0900 Subject: [PATCH 19/20] Change the font color in VIM panel (#349) --- frontend/src/components/editor/editor.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/components/editor/editor.css b/frontend/src/components/editor/editor.css index 9ce14c4e..d7219974 100644 --- a/frontend/src/components/editor/editor.css +++ b/frontend/src/components/editor/editor.css @@ -1,3 +1,7 @@ .wmde-markdown { background-color: transparent; } + +.cm-vim-panel input { + color: inherit; +} From 0a75db5a3b67e08ea26007d91db587e6863f690f Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:39:55 +0900 Subject: [PATCH 20/20] Update `CHANGELOG.md` for `v0.1.12` (#350) --- CHANGELOG.md | 15 ++++++++++++++- backend/package-lock.json | 4 ++-- backend/package.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d3bf426..73ce5785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,20 @@ and CodePair adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] -## [0.1.11] - 20204-09-12 +## [0.1.12] - 2024-09-13 + +### Changed + +- Add missing refresh token settings in `docker-compose-full.yml` by @choidabom in https://github.com/yorkie-team/codepair/pull/344 +- Prevent dragging selection of identical characters in CodeMirror by @choidabom in https://github.com/yorkie-team/codepair/pull/345 +- Reorder keybinding addition for VIM in CodeMirror6 by @devleejb in https://github.com/yorkie-team/codepair/pull/346 + +### Fixed + +- Fix issue where `jj` could not be entered in Vim mode by @hackerwins in https://github.com/yorkie-team/codepair/pull/347 +- Change font color of the panel in VIM mode for improved visibility by @devleejb in https://github.com/yorkie-team/codepair/pull/349 + +## [0.1.11] - 2024-09-12 ### Added diff --git a/backend/package-lock.json b/backend/package-lock.json index bbb81638..b2f8d4da 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-backend", - "version": "0.1.11", + "version": "0.1.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-backend", - "version": "0.1.11", + "version": "0.1.12", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-s3": "^3.509.0", diff --git a/backend/package.json b/backend/package.json index 7cb2867d..561ea3eb 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "codepair-backend", - "version": "0.1.11", + "version": "0.1.12", "description": "CodePair Backend", "author": "yorkie-team", "private": true, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fa89d88f..031d1053 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepair-frontend", - "version": "0.1.11", + "version": "0.1.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepair-frontend", - "version": "0.1.11", + "version": "0.1.12", "license": "Apache-2.0", "dependencies": { "@codemirror/commands": "6.1.2", diff --git a/frontend/package.json b/frontend/package.json index 84630660..04416c06 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,7 +2,7 @@ "name": "codepair-frontend", "description": "CodePair Frontend", "private": true, - "version": "0.1.11", + "version": "0.1.12", "type": "module", "author": "yorkie-team", "license": "Apache-2.0",