feat(user): migrate from gRPC to REST
This commit is contained in:
parent
9160897233
commit
e634d8bd56
23 changed files with 684 additions and 376 deletions
4
Makefile
4
Makefile
|
@ -1,4 +0,0 @@
|
|||
gen:
|
||||
@protoc --proto_path=proto --openapi_out=proto/user/v1 \
|
||||
proto/user/v1/user.proto
|
||||
@pnpm openapi-generator-cli generate -g typescript-axios -i ./proto/user/v1/openapi.yaml -o ./proto/user/v1/api
|
|
@ -1,9 +0,0 @@
|
|||
import {Configuration, UserServiceApi} from "./proto/user/v1/api";
|
||||
|
||||
const configuration = new Configuration({
|
||||
basePath: "http://localhost:60001"
|
||||
})
|
||||
|
||||
const userApi = new UserServiceApi(configuration);
|
||||
|
||||
export {userApi};
|
22
package.json
22
package.json
|
@ -6,27 +6,29 @@
|
|||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"gen": "openapi-generator-cli generate -g typescript-axios -i ./proto/user/v1/openapi.yaml -o ./proto/user/v1/api"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mantine/core": "^7.16.3",
|
||||
"@mantine/dropzone": "^7.16.3",
|
||||
"@mantine/form": "^7.16.3",
|
||||
"@mantine/hooks": "^7.16.3",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"@mantine/core": "^7.17.0",
|
||||
"@mantine/dropzone": "^7.17.0",
|
||||
"@mantine/form": "^7.17.0",
|
||||
"@mantine/hooks": "^7.17.0",
|
||||
"@tabler/icons-react": "^3.30.0",
|
||||
"@tanstack/react-query": "^5.66.3",
|
||||
"@tanstack/react-query": "^5.66.7",
|
||||
"axios": "^1.7.9",
|
||||
"next": "15.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"next": "^15.1.7",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"redux-query": "3.6.1-rc.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openapitools/openapi-generator-cli": "^2.16.3",
|
||||
"@types/jsonwebtoken": "^9.0.9",
|
||||
"@types/node": "^20.17.19",
|
||||
"@types/react": "^19.0.9",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"postcss": "^8.5.2",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
|
|
340
pnpm-lock.yaml
generated
340
pnpm-lock.yaml
generated
|
@ -6,32 +6,32 @@ settings:
|
|||
|
||||
dependencies:
|
||||
'@mantine/core':
|
||||
specifier: ^7.16.3
|
||||
version: 7.16.3(@mantine/hooks@7.16.3)(@types/react@19.0.9)(react-dom@19.0.0)(react@19.0.0)
|
||||
specifier: ^7.17.0
|
||||
version: 7.17.0(@mantine/hooks@7.17.0)(@types/react@19.0.10)(react-dom@19.0.0)(react@19.0.0)
|
||||
'@mantine/dropzone':
|
||||
specifier: ^7.16.3
|
||||
version: 7.16.3(@mantine/core@7.16.3)(@mantine/hooks@7.16.3)(react-dom@19.0.0)(react@19.0.0)
|
||||
specifier: ^7.17.0
|
||||
version: 7.17.0(@mantine/core@7.17.0)(@mantine/hooks@7.17.0)(react-dom@19.0.0)(react@19.0.0)
|
||||
'@mantine/form':
|
||||
specifier: ^7.16.3
|
||||
version: 7.16.3(react@19.0.0)
|
||||
specifier: ^7.17.0
|
||||
version: 7.17.0(react@19.0.0)
|
||||
'@mantine/hooks':
|
||||
specifier: ^7.16.3
|
||||
version: 7.16.3(react@19.0.0)
|
||||
'@reduxjs/toolkit':
|
||||
specifier: ^2.5.1
|
||||
version: 2.5.1(react@19.0.0)
|
||||
specifier: ^7.17.0
|
||||
version: 7.17.0(react@19.0.0)
|
||||
'@tabler/icons-react':
|
||||
specifier: ^3.30.0
|
||||
version: 3.30.0(react@19.0.0)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.66.3
|
||||
version: 5.66.3(react@19.0.0)
|
||||
specifier: ^5.66.7
|
||||
version: 5.66.7(react@19.0.0)
|
||||
axios:
|
||||
specifier: ^1.7.9
|
||||
version: 1.7.9
|
||||
jsonwebtoken:
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2
|
||||
next:
|
||||
specifier: 15.1.0
|
||||
version: 15.1.0(react-dom@19.0.0)(react@19.0.0)
|
||||
specifier: ^15.1.7
|
||||
version: 15.1.7(react-dom@19.0.0)(react@19.0.0)
|
||||
react:
|
||||
specifier: ^19.0.0
|
||||
version: 19.0.0
|
||||
|
@ -46,15 +46,18 @@ devDependencies:
|
|||
'@openapitools/openapi-generator-cli':
|
||||
specifier: ^2.16.3
|
||||
version: 2.16.3
|
||||
'@types/jsonwebtoken':
|
||||
specifier: ^9.0.9
|
||||
version: 9.0.9
|
||||
'@types/node':
|
||||
specifier: ^20.17.19
|
||||
version: 20.17.19
|
||||
'@types/react':
|
||||
specifier: ^19.0.9
|
||||
version: 19.0.9
|
||||
specifier: ^19.0.10
|
||||
version: 19.0.10
|
||||
'@types/react-dom':
|
||||
specifier: ^19.0.3
|
||||
version: 19.0.3(@types/react@19.0.9)
|
||||
specifier: ^19.0.4
|
||||
version: 19.0.4(@types/react@19.0.10)
|
||||
postcss:
|
||||
specifier: ^8.5.2
|
||||
version: 8.5.2
|
||||
|
@ -310,43 +313,43 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/@mantine/core@7.16.3(@mantine/hooks@7.16.3)(@types/react@19.0.9)(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-cxhIpfd2i0Zmk9TKdejYAoIvWouMGhzK3OOX+VRViZ5HEjnTQCGl2h3db56ThqB6NfVPCno6BPbt5lwekTtmuQ==}
|
||||
/@mantine/core@7.17.0(@mantine/hooks@7.17.0)(@types/react@19.0.10)(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-AU5UFewUNzBCUXIq5Jk6q402TEri7atZW61qHW6P0GufJ2W/JxGHRvgmHOVHTVIcuWQRCt9SBSqZoZ/vHs9LhA==}
|
||||
peerDependencies:
|
||||
'@mantine/hooks': 7.16.3
|
||||
'@mantine/hooks': 7.17.0
|
||||
react: ^18.x || ^19.x
|
||||
react-dom: ^18.x || ^19.x
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.26.28(react-dom@19.0.0)(react@19.0.0)
|
||||
'@mantine/hooks': 7.16.3(react@19.0.0)
|
||||
'@mantine/hooks': 7.17.0(react@19.0.0)
|
||||
clsx: 2.1.1
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
react-number-format: 5.4.3(react-dom@19.0.0)(react@19.0.0)
|
||||
react-remove-scroll: 2.6.3(@types/react@19.0.9)(react@19.0.0)
|
||||
react-textarea-autosize: 8.5.6(@types/react@19.0.9)(react@19.0.0)
|
||||
type-fest: 4.34.1
|
||||
react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
|
||||
react-textarea-autosize: 8.5.6(@types/react@19.0.10)(react@19.0.0)
|
||||
type-fest: 4.35.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
dev: false
|
||||
|
||||
/@mantine/dropzone@7.16.3(@mantine/core@7.16.3)(@mantine/hooks@7.16.3)(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-JWKmRMuV0DfgIQWvvtRfokaIopezg2AwxxcXrHs5xxxN1EfiTQWB+aQjz0ISwcAk1gtjLEKHowqsBNbna+BEKw==}
|
||||
/@mantine/dropzone@7.17.0(@mantine/core@7.17.0)(@mantine/hooks@7.17.0)(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-1BGOH/Fs1xxsVl6JUxFAElwqdmtj1nrzc7QSV3vs3xh7zAIAH6wqeor8j8+yycxz4lCfehHSaVAyDDv3AFsX8w==}
|
||||
peerDependencies:
|
||||
'@mantine/core': 7.16.3
|
||||
'@mantine/hooks': 7.16.3
|
||||
'@mantine/core': 7.17.0
|
||||
'@mantine/hooks': 7.17.0
|
||||
react: ^18.x || ^19.x
|
||||
react-dom: ^18.x || ^19.x
|
||||
dependencies:
|
||||
'@mantine/core': 7.16.3(@mantine/hooks@7.16.3)(@types/react@19.0.9)(react-dom@19.0.0)(react@19.0.0)
|
||||
'@mantine/hooks': 7.16.3(react@19.0.0)
|
||||
'@mantine/core': 7.17.0(@mantine/hooks@7.17.0)(@types/react@19.0.10)(react-dom@19.0.0)(react@19.0.0)
|
||||
'@mantine/hooks': 7.17.0(react@19.0.0)
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
react-dropzone-esm: 15.2.0(react@19.0.0)
|
||||
dev: false
|
||||
|
||||
/@mantine/form@7.16.3(react@19.0.0):
|
||||
resolution: {integrity: sha512-GqomUG2Ri5adxYsTU1S5IhKRPcqTG5JkPvMERns8PQAcUz/lvzsnk3wY1v4K5CEbCAdpimle4bSsZTM9g697vg==}
|
||||
/@mantine/form@7.17.0(react@19.0.0):
|
||||
resolution: {integrity: sha512-LONdeb+wL8h9fvyQ339ZFLxqrvYff+b+H+kginZhnr45OBTZDLXNVAt/YoKVFEkynF9WDJjdBVrXKcOZvPgmrA==}
|
||||
peerDependencies:
|
||||
react: ^18.x || ^19.x
|
||||
dependencies:
|
||||
|
@ -355,8 +358,8 @@ packages:
|
|||
react: 19.0.0
|
||||
dev: false
|
||||
|
||||
/@mantine/hooks@7.16.3(react@19.0.0):
|
||||
resolution: {integrity: sha512-B94FBWk5Sc81tAjV+B3dGh/gKzfqzpzVC/KHyBRWOOyJRqeeRbI/FAaJo4zwppyQo1POSl5ArdyjtDRrRIj2SQ==}
|
||||
/@mantine/hooks@7.17.0(react@19.0.0):
|
||||
resolution: {integrity: sha512-vo3K49mLy1nJ8LQNb5KDbJgnX0xwt3Y8JOF3ythjB5LEFMptdLSSgulu64zj+QHtzvffFCsMb05DbTLLpVP/JQ==}
|
||||
peerDependencies:
|
||||
react: ^18.x || ^19.x
|
||||
dependencies:
|
||||
|
@ -426,12 +429,12 @@ packages:
|
|||
- encoding
|
||||
dev: true
|
||||
|
||||
/@next/env@15.1.0:
|
||||
resolution: {integrity: sha512-UcCO481cROsqJuszPPXJnb7GGuLq617ve4xuAyyNG4VSSocJNtMU5Fsx+Lp6mlN8c7W58aZLc5y6D/2xNmaK+w==}
|
||||
/@next/env@15.1.7:
|
||||
resolution: {integrity: sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==}
|
||||
dev: false
|
||||
|
||||
/@next/swc-darwin-arm64@15.1.0:
|
||||
resolution: {integrity: sha512-ZU8d7xxpX14uIaFC3nsr4L++5ZS/AkWDm1PzPO6gD9xWhFkOj2hzSbSIxoncsnlJXB1CbLOfGVN4Zk9tg83PUw==}
|
||||
/@next/swc-darwin-arm64@15.1.7:
|
||||
resolution: {integrity: sha512-hPFwzPJDpA8FGj7IKV3Yf1web3oz2YsR8du4amKw8d+jAOHfYHYFpMkoF6vgSY4W6vB29RtZEklK9ayinGiCmQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
@ -439,8 +442,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-x64@15.1.0:
|
||||
resolution: {integrity: sha512-DQ3RiUoW2XC9FcSM4ffpfndq1EsLV0fj0/UY33i7eklW5akPUCo6OX2qkcLXZ3jyPdo4sf2flwAED3AAq3Om2Q==}
|
||||
/@next/swc-darwin-x64@15.1.7:
|
||||
resolution: {integrity: sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
@ -448,8 +451,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-gnu@15.1.0:
|
||||
resolution: {integrity: sha512-M+vhTovRS2F//LMx9KtxbkWk627l5Q7AqXWWWrfIzNIaUFiz2/NkOFkxCFyNyGACi5YbA8aekzCLtbDyfF/v5Q==}
|
||||
/@next/swc-linux-arm64-gnu@15.1.7:
|
||||
resolution: {integrity: sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
@ -457,8 +460,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-musl@15.1.0:
|
||||
resolution: {integrity: sha512-Qn6vOuwaTCx3pNwygpSGtdIu0TfS1KiaYLYXLH5zq1scoTXdwYfdZtwvJTpB1WrLgiQE2Ne2kt8MZok3HlFqmg==}
|
||||
/@next/swc-linux-arm64-musl@15.1.7:
|
||||
resolution: {integrity: sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
@ -466,8 +469,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-gnu@15.1.0:
|
||||
resolution: {integrity: sha512-yeNh9ofMqzOZ5yTOk+2rwncBzucc6a1lyqtg8xZv0rH5znyjxHOWsoUtSq4cUTeeBIiXXX51QOOe+VoCjdXJRw==}
|
||||
/@next/swc-linux-x64-gnu@15.1.7:
|
||||
resolution: {integrity: sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
@ -475,8 +478,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-musl@15.1.0:
|
||||
resolution: {integrity: sha512-t9IfNkHQs/uKgPoyEtU912MG6a1j7Had37cSUyLTKx9MnUpjj+ZDKw9OyqTI9OwIIv0wmkr1pkZy+3T5pxhJPg==}
|
||||
/@next/swc-linux-x64-musl@15.1.7:
|
||||
resolution: {integrity: sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
@ -484,8 +487,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-arm64-msvc@15.1.0:
|
||||
resolution: {integrity: sha512-WEAoHyG14t5sTavZa1c6BnOIEukll9iqFRTavqRVPfYmfegOAd5MaZfXgOGG6kGo1RduyGdTHD4+YZQSdsNZXg==}
|
||||
/@next/swc-win32-arm64-msvc@15.1.7:
|
||||
resolution: {integrity: sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
@ -493,8 +496,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-x64-msvc@15.1.0:
|
||||
resolution: {integrity: sha512-J1YdKuJv9xcixzXR24Dv+4SaDKc2jj31IVUEMdO5xJivMTXuE6MAdIi4qPjSymHuFG8O5wbfWKnhJUcHHpj5CA==}
|
||||
/@next/swc-win32-x64-msvc@15.1.7:
|
||||
resolution: {integrity: sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
@ -570,24 +573,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@reduxjs/toolkit@2.5.1(react@19.0.0):
|
||||
resolution: {integrity: sha512-UHhy3p0oUpdhnSxyDjaRDYaw8Xra75UiLbCiRozVPHjfDwNYkh0TsVm/1OmTW8Md+iDAJmYPWUKMvsMc2GtpNg==}
|
||||
peerDependencies:
|
||||
react: ^16.9.0 || ^17.0.0 || ^18 || ^19
|
||||
react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
react-redux:
|
||||
optional: true
|
||||
dependencies:
|
||||
immer: 10.1.1
|
||||
react: 19.0.0
|
||||
redux: 5.0.1
|
||||
redux-thunk: 3.1.0(redux@5.0.1)
|
||||
reselect: 5.1.1
|
||||
dev: false
|
||||
|
||||
/@swc/counter@0.1.3:
|
||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||
dev: false
|
||||
|
@ -611,16 +596,16 @@ packages:
|
|||
resolution: {integrity: sha512-c8OKLM48l00u9TFbh2qhSODMONIzML8ajtCyq95rW8vzkWcBrKRPM61tdkThz2j4kd5u17srPGIjqdeRUZdfdw==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/query-core@5.66.3:
|
||||
resolution: {integrity: sha512-+2iDxH7UFdtwcry766aJszGmbByQDIzTltJ3oQAZF9bhCxHCIN3yDwHa6qDCZxcpMGvUphCRx/RYJvLbM8mucQ==}
|
||||
/@tanstack/query-core@5.66.4:
|
||||
resolution: {integrity: sha512-skM/gzNX4shPkqmdTCSoHtJAPMTtmIJNS0hE+xwTTUVYwezArCT34NMermABmBVUg5Ls5aiUXEDXfqwR1oVkcA==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-query@5.66.3(react@19.0.0):
|
||||
resolution: {integrity: sha512-sWMvxZ5VugPDgD1CzP7f0s9yFvjcXP3FXO5IVV2ndXlYqUCwykU8U69Kk05Qn5UvGRqB/gtj4J7vcTC6vtLHtQ==}
|
||||
/@tanstack/react-query@5.66.7(react@19.0.0):
|
||||
resolution: {integrity: sha512-qd3q/tUpF2K1xItfPZddk1k/8pSXnovg41XyCqJgPoyYEirMBtB0sVEVVQ/CsAOngzgWtBPXimVf4q4kM9uO6A==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.66.3
|
||||
'@tanstack/query-core': 5.66.4
|
||||
react: 19.0.0
|
||||
dev: false
|
||||
|
||||
|
@ -628,22 +613,33 @@ packages:
|
|||
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
|
||||
dev: true
|
||||
|
||||
/@types/jsonwebtoken@9.0.9:
|
||||
resolution: {integrity: sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==}
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
'@types/node': 20.17.19
|
||||
dev: true
|
||||
|
||||
/@types/ms@2.1.0:
|
||||
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||
dev: true
|
||||
|
||||
/@types/node@20.17.19:
|
||||
resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==}
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
dev: true
|
||||
|
||||
/@types/react-dom@19.0.3(@types/react@19.0.9):
|
||||
resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==}
|
||||
/@types/react-dom@19.0.4(@types/react@19.0.10):
|
||||
resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==}
|
||||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
dev: true
|
||||
|
||||
/@types/react@19.0.9:
|
||||
resolution: {integrity: sha512-FedNTYgmMwSZmD1Sru/W1gJKuiYCN/3SuBkmZkcxX+FpO5zL76B22A9YNfAKg4HQO3Neh/30AiynP6BELdU0qQ==}
|
||||
/@types/react@19.0.10:
|
||||
resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==}
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
|
@ -728,6 +724,10 @@ packages:
|
|||
fill-range: 7.1.1
|
||||
dev: true
|
||||
|
||||
/buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
dev: false
|
||||
|
||||
/buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
dependencies:
|
||||
|
@ -772,8 +772,8 @@ packages:
|
|||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/caniuse-lite@1.0.30001699:
|
||||
resolution: {integrity: sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==}
|
||||
/caniuse-lite@1.0.30001700:
|
||||
resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==}
|
||||
dev: false
|
||||
|
||||
/chalk@4.1.2:
|
||||
|
@ -986,6 +986,12 @@ packages:
|
|||
wcwidth: 1.0.1
|
||||
dev: true
|
||||
|
||||
/ecdsa-sig-formatter@1.0.11:
|
||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: true
|
||||
|
@ -1259,10 +1265,6 @@ packages:
|
|||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: true
|
||||
|
||||
/immer@10.1.1:
|
||||
resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==}
|
||||
dev: false
|
||||
|
||||
/inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
dev: true
|
||||
|
@ -1380,11 +1382,70 @@ packages:
|
|||
resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==}
|
||||
dev: false
|
||||
|
||||
/jsonwebtoken@9.0.2:
|
||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
dependencies:
|
||||
jws: 3.2.2
|
||||
lodash.includes: 4.3.0
|
||||
lodash.isboolean: 3.0.3
|
||||
lodash.isinteger: 4.0.4
|
||||
lodash.isnumber: 3.0.3
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.isstring: 4.0.1
|
||||
lodash.once: 4.1.1
|
||||
ms: 2.1.3
|
||||
semver: 7.7.1
|
||||
dev: false
|
||||
|
||||
/jwa@1.4.1:
|
||||
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/jws@3.2.2:
|
||||
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
||||
dependencies:
|
||||
jwa: 1.4.1
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/klona@2.0.6:
|
||||
resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: false
|
||||
|
||||
/lodash.includes@4.3.0:
|
||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||
dev: false
|
||||
|
||||
/lodash.isboolean@3.0.3:
|
||||
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
||||
dev: false
|
||||
|
||||
/lodash.isinteger@4.0.4:
|
||||
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
||||
dev: false
|
||||
|
||||
/lodash.isnumber@3.0.3:
|
||||
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
||||
dev: false
|
||||
|
||||
/lodash.isplainobject@4.0.6:
|
||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||
dev: false
|
||||
|
||||
/lodash.isstring@4.0.1:
|
||||
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
||||
dev: false
|
||||
|
||||
/lodash.once@4.1.1:
|
||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||
dev: false
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
@ -1464,7 +1525,6 @@ packages:
|
|||
|
||||
/ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
dev: true
|
||||
|
||||
/mute-stream@0.0.8:
|
||||
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
|
||||
|
@ -1480,8 +1540,8 @@ packages:
|
|||
engines: {node: '>= 0.4.0'}
|
||||
dev: true
|
||||
|
||||
/next@15.1.0(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-QKhzt6Y8rgLNlj30izdMbxAwjHMFANnLwDwZ+WQh5sMhyt4lEBqDK9QpvWHtIM4rINKPoJ8aiRZKg5ULSybVHw==}
|
||||
/next@15.1.7(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-GNeINPGS9c6OZKCvKypbL8GTsT5GhWPp4DM0fzkXJuXMilOO2EeFxuAY6JZbtk6XIl6Ws10ag3xRINDjSO5+wg==}
|
||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -1501,24 +1561,24 @@ packages:
|
|||
sass:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@next/env': 15.1.0
|
||||
'@next/env': 15.1.7
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/helpers': 0.5.15
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: 1.0.30001699
|
||||
caniuse-lite: 1.0.30001700
|
||||
postcss: 8.4.31
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
styled-jsx: 5.1.6(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 15.1.0
|
||||
'@next/swc-darwin-x64': 15.1.0
|
||||
'@next/swc-linux-arm64-gnu': 15.1.0
|
||||
'@next/swc-linux-arm64-musl': 15.1.0
|
||||
'@next/swc-linux-x64-gnu': 15.1.0
|
||||
'@next/swc-linux-x64-musl': 15.1.0
|
||||
'@next/swc-win32-arm64-msvc': 15.1.0
|
||||
'@next/swc-win32-x64-msvc': 15.1.0
|
||||
'@next/swc-darwin-arm64': 15.1.7
|
||||
'@next/swc-darwin-x64': 15.1.7
|
||||
'@next/swc-linux-arm64-gnu': 15.1.7
|
||||
'@next/swc-linux-arm64-musl': 15.1.7
|
||||
'@next/swc-linux-x64-gnu': 15.1.7
|
||||
'@next/swc-linux-x64-musl': 15.1.7
|
||||
'@next/swc-win32-arm64-msvc': 15.1.7
|
||||
'@next/swc-win32-x64-msvc': 15.1.7
|
||||
sharp: 0.33.5
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
|
@ -1574,8 +1634,8 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/pac-proxy-agent@7.1.0:
|
||||
resolution: {integrity: sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==}
|
||||
/pac-proxy-agent@7.2.0:
|
||||
resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==}
|
||||
engines: {node: '>= 14'}
|
||||
dependencies:
|
||||
'@tootallnate/quickjs-emscripten': 0.23.0
|
||||
|
@ -1713,7 +1773,7 @@ packages:
|
|||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
lru-cache: 7.18.3
|
||||
pac-proxy-agent: 7.1.0
|
||||
pac-proxy-agent: 7.2.0
|
||||
proxy-from-env: 1.1.0
|
||||
socks-proxy-agent: 8.0.5
|
||||
transitivePeerDependencies:
|
||||
|
@ -1760,7 +1820,7 @@ packages:
|
|||
react-dom: 19.0.0(react@19.0.0)
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll-bar@2.3.8(@types/react@19.0.9)(react@19.0.0):
|
||||
/react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
|
@ -1770,13 +1830,13 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
react: 19.0.0
|
||||
react-style-singleton: 2.2.3(@types/react@19.0.9)(react@19.0.0)
|
||||
react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0)
|
||||
tslib: 2.8.1
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll@2.6.3(@types/react@19.0.9)(react@19.0.0):
|
||||
/react-remove-scroll@2.6.3(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
|
@ -1786,16 +1846,16 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
react: 19.0.0
|
||||
react-remove-scroll-bar: 2.3.8(@types/react@19.0.9)(react@19.0.0)
|
||||
react-style-singleton: 2.2.3(@types/react@19.0.9)(react@19.0.0)
|
||||
react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0)
|
||||
react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0)
|
||||
tslib: 2.8.1
|
||||
use-callback-ref: 1.3.3(@types/react@19.0.9)(react@19.0.0)
|
||||
use-sidecar: 1.1.3(@types/react@19.0.9)(react@19.0.0)
|
||||
use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0)
|
||||
use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0)
|
||||
dev: false
|
||||
|
||||
/react-style-singleton@2.2.3(@types/react@19.0.9)(react@19.0.0):
|
||||
/react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
|
@ -1805,13 +1865,13 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
get-nonce: 1.0.1
|
||||
react: 19.0.0
|
||||
tslib: 2.8.1
|
||||
dev: false
|
||||
|
||||
/react-textarea-autosize@8.5.6(@types/react@19.0.9)(react@19.0.0):
|
||||
/react-textarea-autosize@8.5.6(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-aT3ioKXMa8f6zHYGebhbdMD2L00tKeRX1zuVuDx9YQK/JLLRSaSxq3ugECEmUB9z2kvk6bFSIoRHLkkUv0RJiw==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
|
@ -1819,8 +1879,8 @@ packages:
|
|||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
react: 19.0.0
|
||||
use-composed-ref: 1.4.0(@types/react@19.0.9)(react@19.0.0)
|
||||
use-latest: 1.3.0(@types/react@19.0.9)(react@19.0.0)
|
||||
use-composed-ref: 1.4.0(@types/react@19.0.10)(react@19.0.0)
|
||||
use-latest: 1.3.0(@types/react@19.0.10)(react@19.0.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
dev: false
|
||||
|
@ -1851,24 +1911,12 @@ packages:
|
|||
reselect: 4.1.8
|
||||
dev: false
|
||||
|
||||
/redux-thunk@3.1.0(redux@5.0.1):
|
||||
resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
|
||||
peerDependencies:
|
||||
redux: ^5.0.0
|
||||
dependencies:
|
||||
redux: 5.0.1
|
||||
dev: false
|
||||
|
||||
/redux@4.2.1:
|
||||
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
dev: false
|
||||
|
||||
/redux@5.0.1:
|
||||
resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
|
||||
dev: false
|
||||
|
||||
/reflect-metadata@0.1.13:
|
||||
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
|
||||
dev: true
|
||||
|
@ -1885,10 +1933,6 @@ packages:
|
|||
resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==}
|
||||
dev: false
|
||||
|
||||
/reselect@5.1.1:
|
||||
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
|
||||
dev: false
|
||||
|
||||
/restore-cursor@3.1.0:
|
||||
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -1928,7 +1972,6 @@ packages:
|
|||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: true
|
||||
|
||||
/safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
|
@ -1944,7 +1987,6 @@ packages:
|
|||
hasBin: true
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
|
@ -2154,8 +2196,8 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/type-fest@4.34.1:
|
||||
resolution: {integrity: sha512-6kSc32kT0rbwxD6QL1CYe8IqdzN/J/ILMrNK+HMQCKH3insCDRY/3ITb0vcBss0a3t72fzh2YSzj8ko1HgwT3g==}
|
||||
/type-fest@4.35.0:
|
||||
resolution: {integrity: sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==}
|
||||
engines: {node: '>=16'}
|
||||
dev: false
|
||||
|
||||
|
@ -2181,7 +2223,7 @@ packages:
|
|||
engines: {node: '>= 10.0.0'}
|
||||
dev: true
|
||||
|
||||
/use-callback-ref@1.3.3(@types/react@19.0.9)(react@19.0.0):
|
||||
/use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
|
@ -2191,12 +2233,12 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
react: 19.0.0
|
||||
tslib: 2.8.1
|
||||
dev: false
|
||||
|
||||
/use-composed-ref@1.4.0(@types/react@19.0.9)(react@19.0.0):
|
||||
/use-composed-ref@1.4.0(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
@ -2205,11 +2247,11 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
react: 19.0.0
|
||||
dev: false
|
||||
|
||||
/use-isomorphic-layout-effect@1.2.0(@types/react@19.0.9)(react@19.0.0):
|
||||
/use-isomorphic-layout-effect@1.2.0(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
@ -2218,11 +2260,11 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
react: 19.0.0
|
||||
dev: false
|
||||
|
||||
/use-latest@1.3.0(@types/react@19.0.9)(react@19.0.0):
|
||||
/use-latest@1.3.0(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
@ -2231,12 +2273,12 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
react: 19.0.0
|
||||
use-isomorphic-layout-effect: 1.2.0(@types/react@19.0.9)(react@19.0.0)
|
||||
use-isomorphic-layout-effect: 1.2.0(@types/react@19.0.10)(react@19.0.0)
|
||||
dev: false
|
||||
|
||||
/use-sidecar@1.1.3(@types/react@19.0.9)(react@19.0.0):
|
||||
/use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0):
|
||||
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
|
@ -2246,7 +2288,7 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 19.0.9
|
||||
'@types/react': 19.0.10
|
||||
detect-node-es: 1.1.0
|
||||
react: 19.0.0
|
||||
tslib: 2.8.1
|
||||
|
|
2
proto
2
proto
|
@ -1 +1 @@
|
|||
Subproject commit 11644adc889ee8f0e0e90c11ccf386a081ee000f
|
||||
Subproject commit b7ea2e6cc71f7393f9afe722204c5c33b6a6f2b6
|
|
@ -1,17 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import {AxiosRequestConfig, AxiosResponse} from "axios";
|
||||
import {userApi} from "../../../configutation";
|
||||
|
||||
const Login = async (username: string, password: string): Promise<AxiosResponse<void>> => {
|
||||
const options: AxiosRequestConfig = {
|
||||
withCredentials: true
|
||||
}
|
||||
|
||||
return await userApi.userServiceLogin({
|
||||
username: username,
|
||||
password: password
|
||||
}, options);
|
||||
}
|
||||
|
||||
export {Login};
|
|
@ -1 +0,0 @@
|
|||
export {LoginPage} from "./ui";
|
|
@ -3,4 +3,4 @@ export const metadata = {
|
|||
description: '',
|
||||
};
|
||||
|
||||
export {LoginPage as default} from "@/_pages/login"
|
||||
export {LoginPage as default} from "@/plain-pages/login"
|
||||
|
|
|
@ -1,35 +1 @@
|
|||
import React from 'react';
|
||||
import {AppShell, AppShellHeader, AppShellMain, Button, Group, Select, Stack, TextInput} from "@mantine/core";
|
||||
import {Header} from "@/components/header";
|
||||
|
||||
type PageProps = {
|
||||
params: {
|
||||
user_id: number
|
||||
}
|
||||
}
|
||||
|
||||
const roles = [
|
||||
"Participant", "Moderator", "Admin"
|
||||
]
|
||||
|
||||
const Page = ({params}: PageProps) => {
|
||||
return (
|
||||
<AppShell header={{height: 70}}>
|
||||
<AppShellHeader>
|
||||
<Header/>
|
||||
</AppShellHeader>
|
||||
<AppShellMain px="16">
|
||||
<Stack align="center">
|
||||
<Group align="end" w="fit-content" m="auto" pt="16" gap="16">
|
||||
<TextInput label="Никнейм" placeholder="Никнейм" defaultValue="user228"/>
|
||||
<Select data={roles} label="Роль" defaultValue={roles[0]} allowDeselect={false}/>
|
||||
<Button label="Пароль">Сменить пароль</Button>
|
||||
</Group>
|
||||
<Button disabled w="fit-content">Сохранить</Button>
|
||||
</Stack>
|
||||
</AppShellMain>
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
export {UserPage as default, generateMetadata} from "@/plain-pages/user";
|
||||
|
|
|
@ -1,99 +1 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
ActionIcon,
|
||||
AppShell,
|
||||
AppShellAside,
|
||||
AppShellHeader,
|
||||
AppShellMain,
|
||||
Pagination,
|
||||
Stack,
|
||||
Table,
|
||||
TableTbody,
|
||||
TableTd,
|
||||
TableTh,
|
||||
TableThead,
|
||||
TableTr,
|
||||
TextInput,
|
||||
Title
|
||||
} from "@mantine/core";
|
||||
import {Header} from "@/components/header";
|
||||
import Link from "next/link";
|
||||
import {IconPencil} from "@tabler/icons-react";
|
||||
|
||||
export const metadata = {
|
||||
title: 'Пользователи',
|
||||
description: '',
|
||||
};
|
||||
|
||||
const users = [
|
||||
{
|
||||
id: 1,
|
||||
username: "user228",
|
||||
email: "use***@gmail.com",
|
||||
role: "Admin",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: "user229",
|
||||
email: "use***@mail.ru",
|
||||
role: "Participant",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
username: "user365",
|
||||
email: "use***@yandex.ru",
|
||||
role: "Moderator",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
username: "user777",
|
||||
email: "use***@yahoo.com",
|
||||
role: "Moderator",
|
||||
},
|
||||
]
|
||||
|
||||
const Page = () => {
|
||||
const rows = users.map((user) => (
|
||||
<TableTr key={user.id}>
|
||||
<TableTd>{user.username}</TableTd>
|
||||
<TableTd>{user.email}</TableTd>
|
||||
<TableTd>{user.role}</TableTd>
|
||||
<TableTd>{<ActionIcon size="xs" component={Link} href={`/users/${user.id}`}>
|
||||
<IconPencil/>
|
||||
</ActionIcon>}
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
));
|
||||
|
||||
return (
|
||||
<AppShell header={{height: 70}}>
|
||||
<AppShellHeader>
|
||||
<Header/>
|
||||
</AppShellHeader>
|
||||
<AppShellMain px="16">
|
||||
<Stack align="center" w="fit-content" m="auto" pt="16" gap="16">
|
||||
<Title>Пользователи</Title>
|
||||
<Table horizontalSpacing="xl">
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Никнейм</TableTh>
|
||||
<TableTh>Почта</TableTh>
|
||||
<TableTh>Роль</TableTh>
|
||||
<TableTh></TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>{rows}</TableTbody>
|
||||
</Table>
|
||||
<Pagination total={10}/>
|
||||
</Stack>
|
||||
</AppShellMain>
|
||||
<AppShellAside withBorder={false} px="16">
|
||||
<Stack pt="16">
|
||||
<TextInput placeholder="Поиск"/>
|
||||
</Stack>
|
||||
</AppShellAside>
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
export {UsersPage as default, generateMetadata} from "@/plain-pages/users";
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import {AxiosRequestConfig, AxiosResponse} from "axios";
|
||||
import {userApi} from "../../../configutation";
|
||||
|
||||
|
||||
const GetMe = async () => {
|
||||
const options: AxiosRequestConfig = {
|
||||
withCredentials: true,
|
||||
timeout: 50,
|
||||
}
|
||||
|
||||
return await userApi.userServiceGetUser(0, true, options);
|
||||
}
|
||||
|
||||
const Logout = async (): Promise<AxiosResponse<void>> => {
|
||||
const options: AxiosRequestConfig = {
|
||||
withCredentials: true
|
||||
}
|
||||
|
||||
return await userApi.userServiceLogout(options);
|
||||
}
|
||||
|
||||
export {GetMe, Logout};
|
|
@ -21,8 +21,8 @@ import NextImage from "next/image"
|
|||
import {IconUser} from "@tabler/icons-react";
|
||||
import {useDisclosure} from "@mantine/hooks";
|
||||
import {useMutation, useQuery} from "@tanstack/react-query";
|
||||
import {GetMe, Logout} from "./api";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {GetMe, Logout} from "@/shared/api";
|
||||
|
||||
const Profile = () => {
|
||||
const router = useRouter();
|
||||
|
@ -31,6 +31,7 @@ const Profile = () => {
|
|||
queryKey: ['me'],
|
||||
queryFn: GetMe,
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const mutation = useMutation({
|
||||
|
|
1
src/plain-pages/login/index.ts
Normal file
1
src/plain-pages/login/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export {Page as LoginPage} from "./page";
|
|
@ -12,27 +12,15 @@ import {
|
|||
Title
|
||||
} from "@mantine/core";
|
||||
import {Header} from "@/components/header";
|
||||
import NextImage from "next/image";
|
||||
import {Login} from "./api";
|
||||
import {useForm} from '@mantine/form';
|
||||
import React from "react";
|
||||
import {useRouter} from 'next/navigation';
|
||||
import {useForm} from "@mantine/form";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import NextImage from "next/image";
|
||||
import {Login} from "@/shared/api";
|
||||
|
||||
|
||||
type Props = {
|
||||
params: {
|
||||
redirectTo: string
|
||||
}
|
||||
}
|
||||
|
||||
type Credentials = {
|
||||
username: string,
|
||||
password: string
|
||||
}
|
||||
|
||||
const Page = (props: Props) => {
|
||||
const Page = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const form = useForm({
|
||||
|
@ -43,15 +31,13 @@ const Page = (props: Props) => {
|
|||
});
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: async (credentials: Credentials) => {
|
||||
await Login(credentials.username, credentials.password)
|
||||
},
|
||||
|
||||
mutationFn: Login,
|
||||
onSuccess: async () => {
|
||||
// FIXME: Possible vulnerability. Should check if the link is from the same resource.
|
||||
await router.push(props.params.redirectTo || "/")
|
||||
await router.push("/")
|
||||
},
|
||||
onError: (error) => {
|
||||
form.clearErrors();
|
||||
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.response?.status === 404) {
|
||||
form.setFieldError("username", "Неверный юзернейм или пароль")
|
||||
|
@ -61,6 +47,7 @@ const Page = (props: Props) => {
|
|||
|
||||
form.setFieldError("username", "Что-то пошло не так. Попробуйте позже.")
|
||||
},
|
||||
retry: false
|
||||
});
|
||||
|
||||
const onSubmit = (event) => {
|
||||
|
@ -82,7 +69,7 @@ const Page = (props: Props) => {
|
|||
justify="center"
|
||||
w="fit-content"
|
||||
m="auto"
|
||||
mt="10%"
|
||||
mt="5%"
|
||||
p="md"
|
||||
style={{color: "var(--mantine-color-bright)"}}
|
||||
>
|
||||
|
@ -120,4 +107,4 @@ const Page = (props: Props) => {
|
|||
);
|
||||
};
|
||||
|
||||
export {Page as LoginPage};
|
||||
export {Page};
|
1
src/plain-pages/user/index.ts
Normal file
1
src/plain-pages/user/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export {UserPage, generateMetadata} from "./page";
|
50
src/plain-pages/user/page.tsx
Normal file
50
src/plain-pages/user/page.tsx
Normal file
|
@ -0,0 +1,50 @@
|
|||
"use server";
|
||||
|
||||
import {Metadata} from "next";
|
||||
import {ClientPage} from "@/plain-pages/user/ui";
|
||||
import {GetUser, ParseJWT} from "@/shared/api";
|
||||
import {notFound} from "next/navigation";
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ user_id: number }>
|
||||
}
|
||||
|
||||
const generateMetadata = async ({params}: Props): Promise<Metadata> => {
|
||||
const user_id = (await params).user_id;
|
||||
|
||||
try {
|
||||
const response = await GetUser(user_id);
|
||||
return {
|
||||
title: `Профиль пользователя ${response.user.username}`,
|
||||
}
|
||||
} catch (e) {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
const ServerPage = async ({params}: Props) => {
|
||||
const user_id = (await params).user_id;
|
||||
|
||||
try {
|
||||
const response = await GetUser(user_id);
|
||||
const jwt = await ParseJWT();
|
||||
|
||||
let canEdit = false;
|
||||
|
||||
if (jwt.user_id === response.user.id && jwt.hasPermission({action: "update", resource: "me-user"})) {
|
||||
canEdit = true;
|
||||
}
|
||||
|
||||
if (jwt.user_id !== response.user.id && jwt.hasPermission({action: "read", resource: "another-user"})) {
|
||||
canEdit = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<ClientPage user={response.user} canEdit={canEdit}/>
|
||||
)
|
||||
} catch (e) {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
export {ServerPage as UserPage, generateMetadata};
|
66
src/plain-pages/user/ui.tsx
Normal file
66
src/plain-pages/user/ui.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
"use client";
|
||||
|
||||
import React, {useState} from 'react';
|
||||
import {AppShell, AppShellHeader, AppShellMain, Button, Group, Select, Stack, TextInput, Title} from "@mantine/core";
|
||||
import {Header} from "@/components/header";
|
||||
import {User} from "../../../proto/user/v1/api";
|
||||
import {UpdateUser} from "@/shared/api";
|
||||
|
||||
|
||||
const roles = [
|
||||
"Студент",
|
||||
"Преподаватель",
|
||||
"Администратор",
|
||||
];
|
||||
|
||||
|
||||
type Props = {
|
||||
user: User
|
||||
canEdit: boolean
|
||||
}
|
||||
|
||||
|
||||
const ClientPage = (props: Props) => {
|
||||
const [user, setUser] = useState(props.user);
|
||||
|
||||
return (
|
||||
<AppShell header={{height: 70}}>
|
||||
<AppShellHeader>
|
||||
<Header/>
|
||||
</AppShellHeader>
|
||||
<AppShellMain px="sm">
|
||||
<Stack align="center" mt="sm">
|
||||
<Title order={2}>
|
||||
Профиль пользователя {props.user.username}
|
||||
</Title>
|
||||
<Group align="end" w="fit-content" m="auto" pt="sm" gap="sm">
|
||||
<TextInput
|
||||
label="Никнейм"
|
||||
placeholder="Никнейм"
|
||||
defaultValue={user.username}
|
||||
onChange={(event) => setUser({...user, username: event.target.value})}
|
||||
disabled={!props.canEdit}
|
||||
/>
|
||||
<Select
|
||||
data={roles}
|
||||
label="Роль"
|
||||
defaultValue={roles[user.role]}
|
||||
allowDeselect={false}
|
||||
onChange={(event) => setUser({...user, role: roles.indexOf(event)})}
|
||||
disabled={!props.canEdit}
|
||||
/>
|
||||
</Group>
|
||||
<Button
|
||||
disabled={JSON.stringify(user) === JSON.stringify(props.user)}
|
||||
w="fit-content"
|
||||
onClick={() => UpdateUser(user.id!, user.role, user.username)}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Stack>
|
||||
</AppShellMain>
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
|
||||
export {ClientPage};
|
1
src/plain-pages/users/index.tsx
Normal file
1
src/plain-pages/users/index.tsx
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./page";
|
29
src/plain-pages/users/page.tsx
Normal file
29
src/plain-pages/users/page.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
"use server";
|
||||
|
||||
import {GetUsers} from "@/shared/api";
|
||||
import {ClientPage} from "./ui";
|
||||
import {Metadata} from "next";
|
||||
|
||||
const generateMetadata = async ({params}: Props): Promise<Metadata> => {
|
||||
return {
|
||||
title: 'Пользователи',
|
||||
description: '',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
type Props = {
|
||||
searchParams: Promise<{ page: number }>
|
||||
}
|
||||
|
||||
const Page = async (props: Props) => {
|
||||
const page = (await props.searchParams).page || 1;
|
||||
|
||||
const users = await GetUsers(page, 10);
|
||||
|
||||
return (
|
||||
<ClientPage users={users.users} max_page={users.max_page} page={users.page}/>
|
||||
)
|
||||
}
|
||||
|
||||
export {Page as UsersPage, generateMetadata};
|
108
src/plain-pages/users/ui.tsx
Normal file
108
src/plain-pages/users/ui.tsx
Normal file
|
@ -0,0 +1,108 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
ActionIcon,
|
||||
AppShell,
|
||||
AppShellAside,
|
||||
AppShellHeader,
|
||||
AppShellMain,
|
||||
Pagination,
|
||||
Stack,
|
||||
Table,
|
||||
TableTbody,
|
||||
TableTd,
|
||||
TableTh,
|
||||
TableThead,
|
||||
TableTr,
|
||||
TextInput,
|
||||
Title
|
||||
} from "@mantine/core";
|
||||
import Link from "next/link";
|
||||
import {IconPencil} from "@tabler/icons-react";
|
||||
import {Header} from "@/components/header";
|
||||
import React from "react";
|
||||
import {User} from "../../../proto/user/v1/api";
|
||||
|
||||
|
||||
type Props = {
|
||||
users: User[],
|
||||
page: number
|
||||
max_page: number
|
||||
};
|
||||
|
||||
const roles = [
|
||||
"Студент",
|
||||
"Преподаватель",
|
||||
"Администратор",
|
||||
];
|
||||
|
||||
|
||||
const ClientPage = (props: Props) => {
|
||||
const rows = props.users.map((user) => (
|
||||
<TableTr key={user.id}>
|
||||
<TableTd>{user.username}</TableTd>
|
||||
{/*<TableTd>{user.email}</TableTd>*/}
|
||||
<TableTd>{roles[user.role]}</TableTd>
|
||||
<TableTd>{<ActionIcon size="xs" component={Link} href={`/users/${user.id}`}>
|
||||
<IconPencil/>
|
||||
</ActionIcon>}
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
));
|
||||
|
||||
return (
|
||||
<AppShell header={{height: 70}}>
|
||||
<AppShellHeader>
|
||||
<Header/>
|
||||
</AppShellHeader>
|
||||
<AppShellMain px="16">
|
||||
<Stack align="center" w="fit-content" m="auto" pt="16" gap="16">
|
||||
<Title>Пользователи</Title>
|
||||
<Table horizontalSpacing="xl">
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Никнейм</TableTh>
|
||||
{/*<TableTh>Почта</TableTh>*/}
|
||||
<TableTh>Роль</TableTh>
|
||||
<TableTh></TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>{rows}</TableTbody>
|
||||
</Table>
|
||||
<Pagination total={props.max_page}
|
||||
value={props.page}
|
||||
getItemProps={(page) => ({
|
||||
component: Link,
|
||||
href: `/users?page=${page}`,
|
||||
})}
|
||||
getControlProps={(control) => {
|
||||
if (control === 'next') {
|
||||
if (props.page === props.max_page) {
|
||||
return {component: Link, href: `/users?page=${props.page}`};
|
||||
}
|
||||
|
||||
return {component: Link, href: `/users?page=${+props.page + 1}`};
|
||||
}
|
||||
|
||||
if (control === 'previous') {
|
||||
if (props.page === 1) {
|
||||
return {component: Link, href: `/users?page=${props.page}`};
|
||||
}
|
||||
return {component: Link, href: `/users?page=${+props.page - 1}`};
|
||||
}
|
||||
|
||||
return {};
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</AppShellMain>
|
||||
<AppShellAside withBorder={false} px="16">
|
||||
<Stack pt="16">
|
||||
<TextInput placeholder="Поиск"/>
|
||||
</Stack>
|
||||
</AppShellAside>
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
|
||||
export {ClientPage};
|
9
src/shared/api/config.ts
Normal file
9
src/shared/api/config.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import {Configuration, DefaultApi} from "../../../proto/user/v1/api";
|
||||
|
||||
const configuration = new Configuration({
|
||||
basePath: "http://localhost:60005",
|
||||
});
|
||||
|
||||
const authApi = new DefaultApi(configuration);
|
||||
|
||||
export {authApi};
|
1
src/shared/api/index.ts
Normal file
1
src/shared/api/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./ms-auth";
|
197
src/shared/api/ms-auth.ts
Normal file
197
src/shared/api/ms-auth.ts
Normal file
|
@ -0,0 +1,197 @@
|
|||
"use server";
|
||||
|
||||
import {AxiosRequestConfig} from "axios";
|
||||
import {decode} from "jsonwebtoken";
|
||||
import {cookies} from "next/headers";
|
||||
import {authApi} from "./config";
|
||||
|
||||
type Credentials = {
|
||||
username: string,
|
||||
password: string
|
||||
}
|
||||
|
||||
const CookieName: any = "SESSIONID";
|
||||
|
||||
type Grant = {
|
||||
action: "create" | "read" | "update" | "delete";
|
||||
resource: "another-user" | "me-user" | "list-user" | "own-session";
|
||||
}
|
||||
|
||||
type JWT = {
|
||||
session_id: string
|
||||
user_id: number
|
||||
role: number
|
||||
exp: number
|
||||
iat: number
|
||||
nbf: number
|
||||
permissions: Grant[]
|
||||
}
|
||||
|
||||
class JWTWithPermissions {
|
||||
public session_id: string
|
||||
public user_id: number
|
||||
public role: number
|
||||
public exp: number
|
||||
public iat: number
|
||||
public nbf: number
|
||||
public permissions: Grant[]
|
||||
|
||||
constructor(jwt: JWT) {
|
||||
this.session_id = jwt.session_id;
|
||||
this.user_id = jwt.user_id;
|
||||
this.role = jwt.role;
|
||||
this.exp = jwt.exp;
|
||||
this.iat = jwt.iat;
|
||||
this.nbf = jwt.nbf;
|
||||
this.permissions = jwt.permissions;
|
||||
}
|
||||
|
||||
public hasPermission(grant: Grant): boolean {
|
||||
return this.permissions.some((permission) => permission.action === grant.action && permission.resource === grant.resource);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const ParseJWT = async () => {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
const session = cookieStore.get(CookieName);
|
||||
|
||||
if (session === undefined) {
|
||||
throw new Error("Session id not found");
|
||||
}
|
||||
|
||||
const token = session.value;
|
||||
|
||||
return new JWTWithPermissions(decode(token) as JWT);
|
||||
}
|
||||
|
||||
|
||||
export const Login = async (credentials: Credentials) => {
|
||||
const options: AxiosRequestConfig = {
|
||||
headers: {
|
||||
"Authorization": "Basic " + btoa(credentials.username + ":" + credentials.password)
|
||||
}
|
||||
}
|
||||
|
||||
const response = await authApi.login(options)
|
||||
|
||||
const token = response.headers["authorization"].split(" ")[1];
|
||||
const decoded = decode(token) as JWT;
|
||||
|
||||
const cookieArgs: any = {
|
||||
httpOnly: true,
|
||||
expires: new Date(decoded["exp"] * 1000),
|
||||
sameSite: 'strict',
|
||||
};
|
||||
|
||||
const cookieStore = await cookies();
|
||||
|
||||
cookieStore.set(CookieName, token, cookieArgs);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
export const GetUser = async (id: number) => {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
const session = cookieStore.get(CookieName);
|
||||
|
||||
if (session === undefined) {
|
||||
throw new Error("Session id not found");
|
||||
}
|
||||
|
||||
const options: AxiosRequestConfig = {
|
||||
headers: {
|
||||
'Authorization': "Bearer " + session.value
|
||||
}
|
||||
};
|
||||
|
||||
const response = await authApi.getUser(id, options);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const UpdateUser = async (id: number, role?: any, username?: string) => {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
const session = cookieStore.get(CookieName);
|
||||
|
||||
if (session === undefined) {
|
||||
throw new Error("Session id not found");
|
||||
}
|
||||
|
||||
const options: AxiosRequestConfig = {
|
||||
headers: {
|
||||
'Authorization': "Bearer " + session.value
|
||||
}
|
||||
};
|
||||
|
||||
const req = {
|
||||
role: role,
|
||||
username: username
|
||||
}
|
||||
|
||||
const response = await authApi.updateUser(id, req, options);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const Logout = async () => {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
const session = cookieStore.get(CookieName);
|
||||
|
||||
if (session === undefined) {
|
||||
throw new Error("Session id not found");
|
||||
}
|
||||
|
||||
const options: AxiosRequestConfig = {
|
||||
headers: {
|
||||
'Authorization': "Bearer " + session.value
|
||||
}
|
||||
};
|
||||
|
||||
const response = await authApi.logout(options)
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const GetMe = async () => {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
const session = cookieStore.get(CookieName);
|
||||
|
||||
if (session === undefined) {
|
||||
throw new Error("Session id not found");
|
||||
}
|
||||
|
||||
const options: AxiosRequestConfig = {
|
||||
headers: {
|
||||
'Authorization': "Bearer " + session.value
|
||||
}
|
||||
};
|
||||
|
||||
const response = await authApi.getMe(options);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const GetUsers = async (page: number, pageSize: number) => {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
const session = cookieStore.get(CookieName);
|
||||
|
||||
if (session === undefined) {
|
||||
throw new Error("Session id not found");
|
||||
}
|
||||
|
||||
const options: AxiosRequestConfig = {
|
||||
headers: {
|
||||
'Authorization': "Bearer " + session.value
|
||||
}
|
||||
};
|
||||
|
||||
const response = await authApi.listUsers(page, pageSize, options);
|
||||
|
||||
return response.data;
|
||||
}
|
Loading…
Add table
Reference in a new issue