diff --git a/.github/workflows/azure-ipam-build.yml b/.github/workflows/azure-ipam-build.yml index 363eb20..e39caa1 100644 --- a/.github/workflows/azure-ipam-build.yml +++ b/.github/workflows/azure-ipam-build.yml @@ -32,6 +32,12 @@ jobs: with: node-version: 18 + - name: "Setup Python v3.9" + id: setupPython + uses: actions/setup-python@v5 + with: + python-version: '3.9' + - name: "Extract Pull Request Details" id: getPullRequestData uses: actions/github-script@v7 @@ -91,6 +97,19 @@ jobs: run: | npm install + - name: Install PIP Packages + id: installPipPackages + working-directory: engine + run: | + pip install -r requirements.txt + + - name: Freeze PIP Packages + id: freezePipPackages + working-directory: engine + run: | + echo "# Dependencies for Azure IPAM v${{ steps.updateVersion.outputs.ipamVersion }}" > requirements.lock.txt + pip freeze >> requirements.lock.txt + - name: "Create Azure IPAM ZIP Asset" id: buildZipAsset working-directory: tools diff --git a/Dockerfile.deb b/Dockerfile.deb index 1d2c765..2f75b6d 100644 --- a/Dockerfile.deb +++ b/Dockerfile.deb @@ -15,7 +15,7 @@ WORKDIR /tmp COPY ./ui/. ./ # Install UI Dependencies -RUN npm install +RUN npm ci RUN chmod 777 -R node_modules # Build IPAM UI @@ -52,13 +52,13 @@ RUN mkdir /var/run/sshd WORKDIR /ipam # Install Engine Dependencies -COPY ./engine/requirements.txt /code/requirements.txt +COPY ./engine/requirements.lock.txt /code/requirements.lock.txt # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt --progress-bar off +RUN pip install --no-cache-dir --upgrade -r /code/requirements.lock.txt --progress-bar off # Copy Engine Code COPY ./engine/app ./app diff --git a/Dockerfile.func b/Dockerfile.func index 559338a..9525570 100644 --- a/Dockerfile.func +++ b/Dockerfile.func @@ -13,7 +13,7 @@ WORKDIR /tmp COPY ./ui/. ./ # Install UI Dependencies -RUN npm install +RUN npm ci RUN chmod 777 -R node_modules # Build IPAM UI @@ -31,13 +31,13 @@ ENV PIP_ROOT_USER_ACTION=ignore WORKDIR /tmp # Copy Requirements File -COPY ./engine/requirements.txt . +COPY ./engine/requirements.lock.txt . # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir --upgrade -r ./requirements.txt --progress-bar off +RUN pip install --no-cache-dir --upgrade -r ./requirements.lock.txt --progress-bar off # Copy Application Code to Function App Root Directory COPY ./engine/. /home/site/wwwroot diff --git a/Dockerfile.rhel b/Dockerfile.rhel index 435334c..4dd0710 100644 --- a/Dockerfile.rhel +++ b/Dockerfile.rhel @@ -18,7 +18,7 @@ USER root COPY ./ui/. ./ # Install UI Dependencies -RUN npm install +RUN npm ci RUN chmod 777 -R node_modules # Build IPAM UI @@ -62,13 +62,13 @@ RUN mkdir /var/run/sshd WORKDIR /ipam # Install Engine Dependencies -COPY ./engine/requirements.txt /code/requirements.txt +COPY ./engine/requirements.lock.txt /code/requirements.lock.txt # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt --progress-bar off +RUN pip install --no-cache-dir --upgrade -r /code/requirements.lock.txt --progress-bar off # Copy Engine Code COPY ./engine/app ./app diff --git a/deploy/deploy.ps1 b/deploy/deploy.ps1 index 00ec2ed..5da035f 100644 --- a/deploy/deploy.ps1 +++ b/deploy/deploy.ps1 @@ -1252,7 +1252,7 @@ process { Write-Host Stop-Transcript | Out-Null - if ($script:deploymentSuccess) { + if (($PSCmdlet.ParameterSetName -notin 'AppsOnly') -and $script:deploymentSuccess) { Write-Output "ipamURL=https://$($deployment.Outputs["appServiceHostName"].Value)" >> $Env:GITHUB_OUTPUT Write-Output "ipamUIAppId=$($appDetails.UIAppId)" >> $Env:GITHUB_OUTPUT Write-Output "ipamEngineAppId=$($appDetails.EngineAppId)" >> $Env:GITHUB_OUTPUT diff --git a/deploy/main.parameters.example.json b/deploy/main.parameters.example.json index e475032..e9c5632 100644 --- a/deploy/main.parameters.example.json +++ b/deploy/main.parameters.example.json @@ -28,9 +28,6 @@ }, "privateAcr": { "value": false - }, - "disableUi": { - "value": false } } } diff --git a/engine/Dockerfile.deb b/engine/Dockerfile.deb index d9aa33b..c659a11 100644 --- a/engine/Dockerfile.deb +++ b/engine/Dockerfile.deb @@ -13,13 +13,13 @@ ENV PIP_ROOT_USER_ACTION=ignore WORKDIR /ipam # Copy Requirements File -ADD ./requirements.txt . +ADD ./requirements.lock.txt . # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir --upgrade -r ./requirements.txt --progress-bar off +RUN pip install --no-cache-dir --upgrade -r ./requirements.lock.txt --progress-bar off # Copy Application Scripts & Sources ADD ./app ./app diff --git a/engine/Dockerfile.func b/engine/Dockerfile.func index 893f2e1..f4f187d 100644 --- a/engine/Dockerfile.func +++ b/engine/Dockerfile.func @@ -6,13 +6,13 @@ ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabled=true # Copy Requirements File -ADD ./requirements.txt . +ADD ./requirements.lock.txt . # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir --upgrade -r ./requirements.txt --progress-bar off +RUN pip install --no-cache-dir --upgrade -r ./requirements.lock.txt --progress-bar off # Copy Application Code to Function App Root Directory COPY . /home/site/wwwroot diff --git a/engine/Dockerfile.rhel b/engine/Dockerfile.rhel index f7744b5..841105e 100644 --- a/engine/Dockerfile.rhel +++ b/engine/Dockerfile.rhel @@ -16,13 +16,13 @@ WORKDIR /ipam USER root # Copy Requirements File -ADD ./requirements.txt /ipam +ADD ./requirements.lock.txt /ipam # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir --upgrade -r ./requirements.txt --progress-bar off +RUN pip install --no-cache-dir --upgrade -r ./requirements.lock.txt --progress-bar off # Copy Application Scripts & Sources ADD ./app ./appDockerfile diff --git a/engine/app/main.py b/engine/app/main.py index 2aaeb3f..4e04aee 100644 --- a/engine/app/main.py +++ b/engine/app/main.py @@ -152,7 +152,10 @@ async def ipam_init(): "env": globals.AZURE_ENV } - requests.post(url = "https://azureipammetrics.azurewebsites.net/api/heartbeat", json = hb_message) + try: + requests.post(url = "https://azureipammetrics.azurewebsites.net/api/heartbeat", json = hb_message) + except Exception: + pass async def upgrade_db(): managed_identity_credential = ManagedIdentityCredential( diff --git a/engine/app/models.py b/engine/app/models.py index cac987d..6176803 100644 --- a/engine/app/models.py +++ b/engine/app/models.py @@ -445,7 +445,7 @@ def validate_request(cls, data: Any) -> Any: return data -class ExtEndpointUpdate(BaseModel): +class ExtEndpointReq(BaseModel): """DOCSTRING""" name: str @@ -463,6 +463,12 @@ class JSONPatch(BaseModel): BlockUpdate = Annotated[List[JSONPatch], None] +ExtNetUpdate = Annotated[List[JSONPatch], None] + +ExtSubnetUpdate = Annotated[List[JSONPatch], None] + +ExtEndpointUpdate = Annotated[List[JSONPatch], None] + VNetsUpdate = Annotated[List[str], None] ExtNetsUpdate = Annotated[List[ExtNet], None] diff --git a/engine/app/routers/admin.py b/engine/app/routers/admin.py index cd866c9..c42e43b 100644 --- a/engine/app/routers/admin.py +++ b/engine/app/routers/admin.py @@ -171,6 +171,39 @@ async def update_admins( return PlainTextResponse(status_code=status.HTTP_200_OK) +@router.get( + "/admins/{objectId}", + summary = "Get IPAM Admin", + response_model = Admin, + status_code = 200 +) +async def get_admins( + objectId: UUID = Path(..., description="Azure AD ObjectID for the target user"), + authorization: str = Header(None, description="Azure Bearer token"), + tenant_id: str = Depends(get_tenant_id), + is_admin: str = Depends(get_admin) +): + """ + Get a specific IPAM admin. + """ + + if not is_admin: + raise HTTPException(status_code=403, detail="API restricted to admins.") + + admin_query = await cosmos_query("SELECT * FROM c WHERE c.type = 'admin'", tenant_id) + + try: + admins = copy.deepcopy(admin_query[0]) + except: + raise HTTPException(status_code=400, detail="No admins found in database.") + + target_admin = next((x for x in admins['admins'] if x['id'] == str(objectId)), None) + + if target_admin: + return target_admin + else: + raise HTTPException(status_code=404, detail="Admin not found.") + @router.delete( "/admins/{objectId}", summary = "Delete IPAM Admin", diff --git a/engine/app/routers/space.py b/engine/app/routers/space.py index ea3a181..ae213eb 100644 --- a/engine/app/routers/space.py +++ b/engine/app/routers/space.py @@ -52,6 +52,8 @@ EXTERNAL_DESC_REGEX = "^(?![ /\._-])([a-zA-Z0-9 /\._-]){1,128}(?=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.11.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.11.0.tgz", - "integrity": "sha512-B6+IKLFs7Lsr06vjX8dPN61ENpTgiFrHf+CVo1UasHcmk5uEOq5D4thrbjsauKX+xtFryYsCDtznVDmWS4/sCg==", + "version": "14.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.13.0.tgz", + "integrity": "sha512-b4M/tqRzJ4jGU91BiwCsLTqChveUEyFK3qY2wGfZ0zBswIBZjAxopx5CYt5wzZFKuN15HqRDYXQbztttuIC3nA==", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-react": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.18.tgz", - "integrity": "sha512-FNGPtmYg31tLBIYjp3i1LVUD+QG7wsT1a+CS4IXcc41Dw5HFQmEFYcozqUM5vH45V7JeIRbzsEk3VSkNtIFwrg==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.20.tgz", + "integrity": "sha512-DnAPmRu8XVIJnvfnpJ6VBDRFa9O2cDXEuHj4ufVrQKJrVAPcjFwo4yYlzuew7Bn39BJFnzxeK+GaKGO+6KHToQ==", "engines": { "node": ">=10" }, "peerDependencies": { - "@azure/msal-browser": "^3.16.0", + "@azure/msal-browser": "^3.18.0", "react": "^16.8.0 || ^17 || ^18" } }, @@ -563,9 +564,9 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -579,9 +580,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -595,9 +596,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -611,9 +612,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -627,9 +628,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -643,9 +644,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -659,9 +660,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -675,9 +676,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -691,9 +692,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -707,9 +708,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -723,9 +724,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -739,9 +740,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -755,9 +756,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -771,9 +772,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -787,9 +788,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -803,9 +804,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -819,9 +820,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -835,9 +836,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -851,9 +852,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -867,9 +868,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -883,9 +884,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -899,9 +900,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -915,9 +916,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -1176,18 +1177,18 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.19.tgz", - "integrity": "sha512-tCHSi/Tomez9ERynFhZRvFO6n9ATyrPs+2N80DMDzp6xDVirbBjEwhPcE+x7Lj+nwYw0SqFkOxyvMP0irnm55w==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.0.tgz", + "integrity": "sha512-8SLffXYPRVpcZx5QzxNE8fytTqzp+IuU3deZbQWg/vSaTlDpR5YVrQ4qQtXTi5cRdhOufV5INylmwlKK+//nPw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.19.tgz", - "integrity": "sha512-RsEiRxA5azN9b8gI7JRqekkgvxQUlitoBOtZglflb8cUDyP12/cP4gRwhb44Ea1/zwwGGjAj66ZJpGHhKfibNA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.0.tgz", + "integrity": "sha512-6ISoOhkp9w5gD0PEW9JklrcbyARDkFWNTBdwXZ1Oy5IGlyu9B0zG0hnUIe4H17IaF1Vgj6C8VI+v4tkSdK0veg==", "dependencies": { "@babel/runtime": "^7.23.9" }, @@ -1210,15 +1211,15 @@ } }, "node_modules/@mui/lab": { - "version": "5.0.0-alpha.170", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.170.tgz", - "integrity": "sha512-0bDVECGmrNjd3+bLdcLiwYZ0O4HP5j5WSQm5DV6iA/Z9kr8O6AnvZ1bv9ImQbbX7Gj3pX4o43EKwCutj3EQxQg==", + "version": "5.0.0-alpha.171", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.171.tgz", + "integrity": "sha512-/ZRnx0wB7hWHMsy76AAUJREVHZ7v5kOKwgJKCQrqOcaPNyo3WiwtTqKaM4Pgj+2r7O10IrC6zOniq8kTRqVAlA==", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/base": "5.0.0-beta.40", - "@mui/system": "^5.15.15", + "@mui/system": "^5.16.0", "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "clsx": "^2.1.0", "prop-types": "^15.8.1" }, @@ -1250,16 +1251,16 @@ } }, "node_modules/@mui/material": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz", - "integrity": "sha512-lp5xQBbcRuxNtjpWU0BWZgIrv2XLUz4RJ0RqFXBdESIsKoGCQZ6P3wwU5ZPuj5TjssNiKv9AlM+vHopRxZhvVQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.0.tgz", + "integrity": "sha512-DbR1NckTLpjt9Zut9EGQ70th86HfN0BYQgyYro6aXQrNfjzSwe3BJS1AyBQ5mJ7TdL6YVRqohfukxj9JlqZZUg==", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/base": "5.0.0-beta.40", - "@mui/core-downloads-tracker": "^5.15.19", - "@mui/system": "^5.15.15", + "@mui/core-downloads-tracker": "^5.16.0", + "@mui/system": "^5.16.0", "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", "csstype": "^3.1.3", @@ -1294,12 +1295,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.0.tgz", + "integrity": "sha512-sYpubkO1MZOnxNyVOClrPNOTs0MfuRVVnAvCeMaOaXt6GimgQbnUcshYv2pSr6PFj+Mqzdff/FYOBceK8u5QgA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "prop-types": "^15.8.1" }, "engines": { @@ -1351,15 +1352,15 @@ } }, "node_modules/@mui/system": { - "version": "5.15.15", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", - "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.0.tgz", + "integrity": "sha512-9YbkC2m3+pNumAvubYv+ijLtog6puJ0fJ6rYfzfLCM47pWrw3m+30nXNM8zMgDaKL6vpfWJcCXm+LPaWBpy7sw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.14", + "@mui/private-theming": "^5.16.0", "@mui/styled-engine": "^5.15.14", "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1403,9 +1404,9 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.0.tgz", + "integrity": "sha512-kLLi5J1xY+mwtUlMb8Ubdxf4qFAA1+U7WPBvjM/qQ4CIwLCohNb0sHo1oYPufjSIH/Z9+dhVxD7dJlfGjd1AVA==", "dependencies": { "@babel/runtime": "^7.23.9", "@types/prop-types": "^15.7.11", @@ -1474,9 +1475,9 @@ } }, "node_modules/@reduxjs/toolkit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz", - "integrity": "sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", + "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", "dependencies": { "immer": "^10.0.3", "redux": "^5.0.1", @@ -1497,9 +1498,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", - "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.1.tgz", + "integrity": "sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==", "engines": { "node": ">=14.0.0" } @@ -1824,11 +1825,11 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.5.tgz", - "integrity": "sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.6.tgz", + "integrity": "sha512-8qpnGVincVDLEcQXWaHOf6zmlbwTKc6Us6PPu4CRnPXCzo2OGBS5cwgMMOWdxDpEz1mkbvXHpEy99M5Yvt682w==", "dependencies": { - "@adobe/css-tools": "^4.3.2", + "@adobe/css-tools": "^4.4.0", "@babel/runtime": "^7.9.2", "aria-query": "^5.0.0", "chalk": "^3.0.0", @@ -2111,9 +2112,9 @@ "dev": true }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.0.tgz", - "integrity": "sha512-KcEbMsn4Dpk+LIbHMj7gDPRKaTMStxxWRkRmxsg/jVdFdJCZWt1SchZcf0M4t8lIKdwwMsEyzhrcOXRrDPtOBw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", "dev": true, "dependencies": { "@babel/core": "^7.24.5", @@ -2752,6 +2753,14 @@ "node": ">=0.8.0" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2943,6 +2952,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -3119,12 +3136,12 @@ "dev": true }, "node_modules/echarts": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", - "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", "dependencies": { "tslib": "2.3.0", - "zrender": "5.5.0" + "zrender": "5.6.0" } }, "node_modules/echarts-for-react": { @@ -3319,9 +3336,9 @@ } }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -3331,29 +3348,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { @@ -3432,16 +3449,16 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "version": "7.34.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", + "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", @@ -4322,6 +4339,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -4888,6 +4910,16 @@ "lz-string": "bin/bin.js" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5358,9 +5390,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "dev": true, "funding": [ { @@ -5378,7 +5410,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -5586,11 +5618,11 @@ } }, "node_modules/react-router": { - "version": "6.23.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", - "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.1.tgz", + "integrity": "sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==", "dependencies": { - "@remix-run/router": "1.16.1" + "@remix-run/router": "1.17.1" }, "engines": { "node": ">=14.0.0" @@ -5600,12 +5632,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.23.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", - "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.1.tgz", + "integrity": "sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==", "dependencies": { - "@remix-run/router": "1.16.1", - "react-router": "6.23.1" + "@remix-run/router": "1.17.1", + "react-router": "6.24.1" }, "engines": { "node": ">=14.0.0" @@ -6541,13 +6573,13 @@ } }, "node_modules/vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", + "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", "dev": true, "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", + "esbuild": "^0.21.3", + "postcss": "^8.4.39", "rollup": "^4.13.0" }, "bin": { @@ -6621,9 +6653,9 @@ } }, "node_modules/web-vitals": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.0.1.tgz", - "integrity": "sha512-AW6qT/vXK3pbf+WgVcEBXY//AWCpXjVKgdb6rt0cARSUdtT+NUtZCOeo+CSLUX7PjSQ275DmxfkAs7QlPbtR6w==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.1.tgz", + "integrity": "sha512-U6bAxeudnhDqcXNl50JC4hLlqox9DZnngxfISZm3DMZnonW35xtJOVUc091L+DOY+6hVZVpKXoiCP0RiT6339Q==" }, "node_modules/which": { "version": "2.0.2", @@ -6832,9 +6864,9 @@ } }, "node_modules/zrender": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", - "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", "dependencies": { "tslib": "2.3.0" } @@ -6857,22 +6889,22 @@ } }, "@azure/msal-browser": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.16.0.tgz", - "integrity": "sha512-WKobvIisBK7sFSOwHuchH9tUMekwhJRLgLE9tKhIq0wFYGRcVGK0KivP5vZrobVZEMNCZWto0fI1VcSVoa+cig==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.18.0.tgz", + "integrity": "sha512-jvK5bDUWbpOaJt2Io/rjcaOVcUzkqkrCme/WntdV1SMUc67AiTcEdKuY6G/nMQ7N5Cfsk9SfpugflQwDku53yg==", "requires": { - "@azure/msal-common": "14.11.0" + "@azure/msal-common": "14.13.0" } }, "@azure/msal-common": { - "version": "14.11.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.11.0.tgz", - "integrity": "sha512-B6+IKLFs7Lsr06vjX8dPN61ENpTgiFrHf+CVo1UasHcmk5uEOq5D4thrbjsauKX+xtFryYsCDtznVDmWS4/sCg==" + "version": "14.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.13.0.tgz", + "integrity": "sha512-b4M/tqRzJ4jGU91BiwCsLTqChveUEyFK3qY2wGfZ0zBswIBZjAxopx5CYt5wzZFKuN15HqRDYXQbztttuIC3nA==" }, "@azure/msal-react": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.18.tgz", - "integrity": "sha512-FNGPtmYg31tLBIYjp3i1LVUD+QG7wsT1a+CS4IXcc41Dw5HFQmEFYcozqUM5vH45V7JeIRbzsEk3VSkNtIFwrg==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.20.tgz", + "integrity": "sha512-DnAPmRu8XVIJnvfnpJ6VBDRFa9O2cDXEuHj4ufVrQKJrVAPcjFwo4yYlzuew7Bn39BJFnzxeK+GaKGO+6KHToQ==", "requires": {} }, "@babel/code-frame": { @@ -7236,163 +7268,163 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "dev": true, "optional": true }, "@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "dev": true, "optional": true }, @@ -7569,43 +7601,43 @@ } }, "@mui/core-downloads-tracker": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.19.tgz", - "integrity": "sha512-tCHSi/Tomez9ERynFhZRvFO6n9ATyrPs+2N80DMDzp6xDVirbBjEwhPcE+x7Lj+nwYw0SqFkOxyvMP0irnm55w==" + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.0.tgz", + "integrity": "sha512-8SLffXYPRVpcZx5QzxNE8fytTqzp+IuU3deZbQWg/vSaTlDpR5YVrQ4qQtXTi5cRdhOufV5INylmwlKK+//nPw==" }, "@mui/icons-material": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.19.tgz", - "integrity": "sha512-RsEiRxA5azN9b8gI7JRqekkgvxQUlitoBOtZglflb8cUDyP12/cP4gRwhb44Ea1/zwwGGjAj66ZJpGHhKfibNA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.0.tgz", + "integrity": "sha512-6ISoOhkp9w5gD0PEW9JklrcbyARDkFWNTBdwXZ1Oy5IGlyu9B0zG0hnUIe4H17IaF1Vgj6C8VI+v4tkSdK0veg==", "requires": { "@babel/runtime": "^7.23.9" } }, "@mui/lab": { - "version": "5.0.0-alpha.170", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.170.tgz", - "integrity": "sha512-0bDVECGmrNjd3+bLdcLiwYZ0O4HP5j5WSQm5DV6iA/Z9kr8O6AnvZ1bv9ImQbbX7Gj3pX4o43EKwCutj3EQxQg==", + "version": "5.0.0-alpha.171", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.171.tgz", + "integrity": "sha512-/ZRnx0wB7hWHMsy76AAUJREVHZ7v5kOKwgJKCQrqOcaPNyo3WiwtTqKaM4Pgj+2r7O10IrC6zOniq8kTRqVAlA==", "requires": { "@babel/runtime": "^7.23.9", "@mui/base": "5.0.0-beta.40", - "@mui/system": "^5.15.15", + "@mui/system": "^5.16.0", "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "clsx": "^2.1.0", "prop-types": "^15.8.1" } }, "@mui/material": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz", - "integrity": "sha512-lp5xQBbcRuxNtjpWU0BWZgIrv2XLUz4RJ0RqFXBdESIsKoGCQZ6P3wwU5ZPuj5TjssNiKv9AlM+vHopRxZhvVQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.0.tgz", + "integrity": "sha512-DbR1NckTLpjt9Zut9EGQ70th86HfN0BYQgyYro6aXQrNfjzSwe3BJS1AyBQ5mJ7TdL6YVRqohfukxj9JlqZZUg==", "requires": { "@babel/runtime": "^7.23.9", "@mui/base": "5.0.0-beta.40", - "@mui/core-downloads-tracker": "^5.15.19", - "@mui/system": "^5.15.15", + "@mui/core-downloads-tracker": "^5.16.0", + "@mui/system": "^5.16.0", "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", "csstype": "^3.1.3", @@ -7615,12 +7647,12 @@ } }, "@mui/private-theming": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.0.tgz", + "integrity": "sha512-sYpubkO1MZOnxNyVOClrPNOTs0MfuRVVnAvCeMaOaXt6GimgQbnUcshYv2pSr6PFj+Mqzdff/FYOBceK8u5QgA==", "requires": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "prop-types": "^15.8.1" } }, @@ -7636,15 +7668,15 @@ } }, "@mui/system": { - "version": "5.15.15", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", - "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.0.tgz", + "integrity": "sha512-9YbkC2m3+pNumAvubYv+ijLtog6puJ0fJ6rYfzfLCM47pWrw3m+30nXNM8zMgDaKL6vpfWJcCXm+LPaWBpy7sw==", "requires": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.14", + "@mui/private-theming": "^5.16.0", "@mui/styled-engine": "^5.15.14", "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.0", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -7657,9 +7689,9 @@ "requires": {} }, "@mui/utils": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.0.tgz", + "integrity": "sha512-kLLi5J1xY+mwtUlMb8Ubdxf4qFAA1+U7WPBvjM/qQ4CIwLCohNb0sHo1oYPufjSIH/Z9+dhVxD7dJlfGjd1AVA==", "requires": { "@babel/runtime": "^7.23.9", "@types/prop-types": "^15.7.11", @@ -7699,9 +7731,9 @@ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@reduxjs/toolkit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz", - "integrity": "sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", + "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", "requires": { "immer": "^10.0.3", "redux": "^5.0.1", @@ -7710,9 +7742,9 @@ } }, "@remix-run/router": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", - "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==" + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.1.tgz", + "integrity": "sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==" }, "@rollup/pluginutils": { "version": "5.1.0", @@ -7905,11 +7937,11 @@ } }, "@testing-library/jest-dom": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.5.tgz", - "integrity": "sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.6.tgz", + "integrity": "sha512-8qpnGVincVDLEcQXWaHOf6zmlbwTKc6Us6PPu4CRnPXCzo2OGBS5cwgMMOWdxDpEz1mkbvXHpEy99M5Yvt682w==", "requires": { - "@adobe/css-tools": "^4.3.2", + "@adobe/css-tools": "^4.4.0", "@babel/runtime": "^7.9.2", "aria-query": "^5.0.0", "chalk": "^3.0.0", @@ -8123,9 +8155,9 @@ "dev": true }, "@vitejs/plugin-react": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.0.tgz", - "integrity": "sha512-KcEbMsn4Dpk+LIbHMj7gDPRKaTMStxxWRkRmxsg/jVdFdJCZWt1SchZcf0M4t8lIKdwwMsEyzhrcOXRrDPtOBw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", "dev": true, "requires": { "@babel/core": "^7.24.5", @@ -8570,6 +8602,11 @@ } } }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, "chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -8721,6 +8758,11 @@ "which": "^2.0.1" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, "css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -8847,12 +8889,12 @@ "dev": true }, "echarts": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", - "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", "requires": { "tslib": "2.3.0", - "zrender": "5.5.0" + "zrender": "5.6.0" } }, "echarts-for-react": { @@ -9016,34 +9058,34 @@ } }, "esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, - "requires": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "escalade": { @@ -9170,16 +9212,16 @@ } }, "eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "version": "7.34.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", + "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", "dev": true, "requires": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", @@ -9735,6 +9777,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -10130,6 +10177,16 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "peer": true }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -10452,13 +10509,13 @@ "dev": true }, "postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "dev": true, "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" } }, @@ -10607,20 +10664,20 @@ "dev": true }, "react-router": { - "version": "6.23.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", - "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.1.tgz", + "integrity": "sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==", "requires": { - "@remix-run/router": "1.16.1" + "@remix-run/router": "1.17.1" } }, "react-router-dom": { - "version": "6.23.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", - "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.1.tgz", + "integrity": "sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==", "requires": { - "@remix-run/router": "1.16.1", - "react-router": "6.23.1" + "@remix-run/router": "1.17.1", + "react-router": "6.24.1" } }, "react-transition-group": { @@ -11288,14 +11345,14 @@ "dev": true }, "vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", + "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", "dev": true, "requires": { - "esbuild": "^0.20.1", + "esbuild": "^0.21.3", "fsevents": "~2.3.3", - "postcss": "^8.4.38", + "postcss": "^8.4.39", "rollup": "^4.13.0" } }, @@ -11311,9 +11368,9 @@ } }, "web-vitals": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.0.1.tgz", - "integrity": "sha512-AW6qT/vXK3pbf+WgVcEBXY//AWCpXjVKgdb6rt0cARSUdtT+NUtZCOeo+CSLUX7PjSQ275DmxfkAs7QlPbtR6w==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.1.tgz", + "integrity": "sha512-U6bAxeudnhDqcXNl50JC4hLlqox9DZnngxfISZm3DMZnonW35xtJOVUc091L+DOY+6hVZVpKXoiCP0RiT6339Q==" }, "which": { "version": "2.0.2", @@ -11455,9 +11512,9 @@ "dev": true }, "zrender": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", - "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", "requires": { "tslib": "2.3.0" } diff --git a/ui/package.json b/ui/package.json index a6fb057..eebcfb0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -4,22 +4,23 @@ "type": "module", "private": true, "dependencies": { - "@azure/msal-browser": "^3.16.0", - "@azure/msal-react": "^2.0.18", + "@azure/msal-browser": "^3.18.0", + "@azure/msal-react": "^2.0.20", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@inovua/reactdatagrid-community": "^5.10.2", - "@mui/icons-material": "^5.15.19", - "@mui/lab": "^5.0.0-alpha.170", - "@mui/material": "^5.15.19", - "@reduxjs/toolkit": "^2.2.5", - "@testing-library/jest-dom": "^6.4.5", + "@mui/icons-material": "^5.16.0", + "@mui/lab": "^5.0.0-alpha.171", + "@mui/material": "^5.16.0", + "@reduxjs/toolkit": "^2.2.6", + "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", "axios": "^1.7.2", - "echarts": "^5.5.0", + "echarts": "^5.5.1", "echarts-for-react": "^3.0.2", "lodash": "^4.17.21", + "md5": "^2.3.0", "moment": "^2.30.1", "notistack": "^3.0.1", "pluralize": "^8.0.0", @@ -27,9 +28,9 @@ "react-dom": "^18.3.1", "react-draggable": "^4.4.6", "react-redux": "^9.1.2", - "react-router-dom": "^6.23.1", + "react-router-dom": "^6.24.1", "spinners-react": "^1.0.7", - "web-vitals": "^4.0.1" + "web-vitals": "^4.2.1" }, "scripts": { "start": "vite", @@ -48,12 +49,12 @@ ] }, "devDependencies": { - "@vitejs/plugin-react": "^4.3.0", + "@vitejs/plugin-react": "^4.3.1", "eslint": "^8.57.0", - "eslint-plugin-react": "^7.34.2", + "eslint-plugin-react": "^7.34.3", "eslint-plugin-react-hooks": "^4.6.2", "serve": "^14.2.3", - "vite": "^5.2.12", + "vite": "^5.3.3", "vite-plugin-eslint2": "^4.4.0" } } diff --git a/ui/src/features/configure/associations/associations.jsx b/ui/src/features/configure/associations/associations.jsx index 5f68012..3803e3d 100644 --- a/ui/src/features/configure/associations/associations.jsx +++ b/ui/src/features/configure/associations/associations.jsx @@ -47,7 +47,8 @@ import { selectSubscriptions, fetchNetworksAsync, selectViewSetting, - updateMeAsync + updateMeAsync, + getAdminStatus } from "../../ipam/ipamSlice"; const vNetPattern = "/Microsoft.Network/virtualNetworks/"; @@ -256,6 +257,7 @@ const Associations = () => { const [unchanged, setUnchanged] = React.useState(true); + const isAdmin = useSelector(getAdminStatus); const spaces = useSelector(selectSpaces); const blocks = useSelector(selectBlocks); const subscriptions = useSelector(selectSubscriptions); @@ -859,7 +861,7 @@ const Associations = () => { theme={theme.palette.mode === 'dark' ? "default-dark" : "default-light"} idProperty="id" showCellBorders="horizontal" - checkboxColumn + checkboxColumn={isAdmin} checkboxOnlyRowSelect showZebraRows={false} multiSelect={true} @@ -881,7 +883,7 @@ const Associations = () => { loadingText={sending ? Updating : "Loading"} dataSource={gridData || []} selected={selectionModel || []} - onSelectionChange={({selected}) => setSelection(selected)} + onSelectionChange={({selected}) => isAdmin && setSelection(selected)} rowClassName={({data}) => `ipam-block-vnet-${!data.active ? 'stale' : 'normal'}`} onCellDoubleClick={onCellDoubleClick} sortInfo={columnSortState} diff --git a/ui/src/features/configure/basics/block/block.jsx b/ui/src/features/configure/basics/block/block.jsx index cebacbf..9bf5820 100644 --- a/ui/src/features/configure/basics/block/block.jsx +++ b/ui/src/features/configure/basics/block/block.jsx @@ -271,39 +271,33 @@ export default function BlockDataGrid(props) { }, }} > - { isAdmin && - - - - - Add Block - - } - { isAdmin && - - - - - Edit Block - - } - { isAdmin && - navigate('/configure/associations', {state: { space: selectedSpace, block: selectedBlock }})} - disabled={!selectedBlock} - > - - - - Block Networks - - } + + + + + Add Block + + + + + + Edit Block + + navigate('/configure/associations', {state: { space: selectedSpace, block: selectedBlock }})} + disabled={!selectedBlock} + > + + + + Block Networks + navigate('/configure/reservations', {state: { space: selectedSpace, block: selectedBlock }})} disabled={!selectedBlock} @@ -313,31 +307,25 @@ export default function BlockDataGrid(props) { Reservations - { isAdmin && - navigate('/configure/externals', {state: { space: selectedSpace, block: selectedBlock }})} - disabled={!selectedBlock} - > - - - - External Networks - - } - { isAdmin && - - } - { isAdmin && - - - - - Delete - - } + navigate('/configure/externals', {state: { space: selectedSpace, block: selectedBlock }})} + disabled={!selectedBlock} + > + + + + External Networks + + + + + + + Delete + diff --git a/ui/src/features/configure/basics/space/space.jsx b/ui/src/features/configure/basics/space/space.jsx index c424876..1b7ba1a 100644 --- a/ui/src/features/configure/basics/space/space.jsx +++ b/ui/src/features/configure/basics/space/space.jsx @@ -156,25 +156,25 @@ export default function SpaceDataGrid(props) { { isAdmin && - setEditSpaceOpen(false)} - space={selectedSpace ? selectedSpace : null} - spaces={spaces} - refresh={refresh} - /> - setAddSpaceOpen(false)} - spaces={spaces} - refresh={refresh} - /> - setDeleteSpaceOpen(false)} - space={selectedSpace ? selectedSpace.name : null} - refresh={refresh} - /> + setEditSpaceOpen(false)} + space={selectedSpace ? selectedSpace : null} + spaces={spaces} + refresh={refresh} + /> + setAddSpaceOpen(false)} + spaces={spaces} + refresh={refresh} + /> + setDeleteSpaceOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + refresh={refresh} + /> } {selectedSpace ? `'${selectedSpace.name}' selected` : "Spaces"} - { isAdmin && - - - - - - - + + + + + + + + + {/* */} + + + + + Add Space + + + + + + Edit Space + + + - - - {/* */} - - - - - Add Space - - - - - - Edit Space - - - - - - - Delete - - - - } + + + + Delete + + + diff --git a/ui/src/features/configure/externals/networks/networks.jsx b/ui/src/features/configure/externals/networks/networks.jsx index 483a74d..dedec8d 100644 --- a/ui/src/features/configure/externals/networks/networks.jsx +++ b/ui/src/features/configure/externals/networks/networks.jsx @@ -29,16 +29,18 @@ import { TaskAltOutlined, CancelOutlined, AddOutlined, - // EditOutlined, + EditOutlined, DeleteOutline } from "@mui/icons-material"; import { selectViewSetting, - updateMeAsync + updateMeAsync, + getAdminStatus } from "../../../ipam/ipamSlice"; import AddExtNetwork from "./utils/addNetwork"; +import EditExtNetwork from "./utils/editNetwork"; import DeleteExtNetwork from "./utils/deleteNetwork"; import { ExternalContext } from "../externalContext"; @@ -58,6 +60,7 @@ function HeaderMenu(props) { selectedBlock, selectedExternal, setAddExtOpen, + setEditExtOpen, setDelExtOpen, saving, sendResults, @@ -70,6 +73,7 @@ function HeaderMenu(props) { const menuRef = React.useRef(null); + const isAdmin = useSelector(getAdminStatus); const viewSetting = useSelector(state => selectViewSetting(state, setting)); const onClick = () => { @@ -81,6 +85,11 @@ function HeaderMenu(props) { setMenuOpen(false); } + const onEditExt = () => { + setEditExtOpen(true); + setMenuOpen(false); + } + const onDelExt = () => { setDelExtOpen(true); setMenuOpen(false); @@ -176,25 +185,25 @@ function HeaderMenu(props) { > Add Network - {/* console.log("EDIT!")} - disabled={ true } + Edit Network - */} + @@ -253,8 +262,10 @@ const Networks = (props) => { const [columnSortState, setColumnSortState] = React.useState({}); const [addExtOpen, setAddExtOpen] = React.useState(false); + const [editExtOpen, setEditExtOpen] = React.useState(false); const [delExtOpen, setDelExtOpen] = React.useState(false); + const isAdmin = useSelector(getAdminStatus); const viewSetting = useSelector(state => selectViewSetting(state, 'extnetworks')); const saveTimer = React.useRef(); @@ -479,21 +490,33 @@ const Networks = (props) => { return ( - setAddExtOpen(false)} - space={selectedSpace ? selectedSpace.name : null} - block={selectedBlock ? selectedBlock : null} - externals={externals} - /> - setDelExtOpen(false)} - space={selectedSpace ? selectedSpace.name : null} - block={selectedBlock ? selectedBlock.name : null} - external={selectedExternal ? selectedExternal.name : null} - /> - + { isAdmin && + + setAddExtOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + block={selectedBlock ? selectedBlock : null} + externals={externals} + /> + setEditExtOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + block={selectedBlock ? selectedBlock : null} + externals={externals} + selectedExternal={selectedExternal} + /> + setDelExtOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + block={selectedBlock ? selectedBlock.name : null} + external={selectedExternal ? selectedExternal.name : null} + /> + + } + diff --git a/ui/src/features/configure/externals/networks/utils/editNetwork.jsx b/ui/src/features/configure/externals/networks/utils/editNetwork.jsx new file mode 100644 index 0000000..e53ca9c --- /dev/null +++ b/ui/src/features/configure/externals/networks/utils/editNetwork.jsx @@ -0,0 +1,363 @@ +import * as React from "react"; +import { useSelector, useDispatch } from "react-redux"; + +import { useSnackbar } from "notistack"; + +import Draggable from "react-draggable"; + +import { + Box, + Button, + Tooltip, + TextField, + Dialog, + DialogTitle, + DialogActions, + DialogContent, + Paper +} from "@mui/material"; + +import LoadingButton from "@mui/lab/LoadingButton"; + +import { + selectNetworks, + updateBlockExternalAsync +} from "../../../../ipam/ipamSlice"; + +import { + isSubnetOf, + isSubnetOverlap +} from "../../../../tools/planner/utils/iputils"; + +import { + EXTERNAL_NAME_REGEX, + EXTERNAL_DESC_REGEX, + CIDR_REGEX +} from "../../../../../global/globals"; + +function DraggablePaper(props) { + const nodeRef = React.useRef(null); + + return ( + + + + ); +} + +export default function EditExtNetwork(props) { + const { open, handleClose, space, block, externals, selectedExternal } = props; + + const { enqueueSnackbar } = useSnackbar(); + + const [extName, setExtName] = React.useState({ value: "", error: false }); + const [extDesc, setExtDesc] = React.useState({ value: "", error: false }); + const [extCidr, setExtCidr] = React.useState({ value: "", error: false }); + + const [sending, setSending] = React.useState(false); + + const dispatch = useDispatch(); + + const networks = useSelector(selectNetworks); + + function onCancel() { + handleClose(); + + if(selectedExternal) { + setExtName({ value: selectedExternal.name, error: false }); + setExtDesc({ value: selectedExternal.desc, error: false }); + setExtCidr({ value: selectedExternal.cidr, error: false }); + } else { + setExtName({ value: "", error: false }); + setExtDesc({ value: "", error: false }); + setExtCidr({ value: "", error: false }); + } + } + + function onSubmit() { + var body = [ + { + op: "replace", + path: "/name", + value: extName.value + }, + { + op: "replace", + path: "/desc", + value: extDesc.value, + }, + { + op: "replace", + path: "/cidr", + value: extCidr.value + } + ]; + + (async () => { + try { + setSending(true); + await dispatch(updateBlockExternalAsync({ space: space, block: block.name, external: selectedExternal.name, body: body })); + enqueueSnackbar("Successfully updated External Network", { variant: "success" }); + onCancel(); + } catch (e) { + console.log("ERROR"); + console.log("------------------"); + console.log(e); + console.log("------------------"); + enqueueSnackbar(e.message, { variant: "error" }); + } finally { + setSending(false); + } + })(); + } + + function onNameChange(event) { + const newName = event.target.value; + + if(externals) { + const regex = new RegExp( + EXTERNAL_NAME_REGEX + ); + + const nameError = newName ? !regex.test(newName) : false; + const nameExists = externals?.reduce((acc, curr) => { + curr['name'] !== selectedExternal['name'] && acc.push(curr['name'].toLowerCase()); + + return acc; + }, []).includes(newName.toLowerCase()); + + setExtName({ + value: newName, + error: (nameError || nameExists) + }); + } + } + + function onDescChange(event) { + const newDesc = event.target.value; + + const regex = new RegExp( + EXTERNAL_DESC_REGEX + ); + + setExtDesc({ + value: newDesc, + error: (newDesc ? !regex.test(newDesc) : false) + }); + } + + function onCidrChange(event) { + const newCidr = event.target.value; + + const regex = new RegExp( + CIDR_REGEX + ); + + const cidrError = newCidr ? !regex.test(newCidr) : false; + + var blockNetworks= []; + var extNetworks = []; + + var cidrInBlock = false; + var resvOverlap = true; + var vnetOverlap = true; + var extOverlap = true; + + if(!cidrError && newCidr.length > 0) { + cidrInBlock = isSubnetOf(newCidr, block.cidr); + + const openResv = block?.resv.reduce((acc, curr) => { + if(!curr['settledOn']) { + acc.push(curr['cidr']); + } + + return acc; + }, []); + + if(space && block && networks) { + blockNetworks = networks?.reduce((acc, curr) => { + if(curr['parent_space'] && curr['parent_block']) { + if(curr['parent_space'] === space && curr['parent_block'].includes(block.name)) { + acc = acc.concat(curr['prefixes']); + } + } + + return acc; + }, []); + } + + if(externals) { + extNetworks = externals?.reduce((acc, curr) => { + curr['name'] !== selectedExternal['name'] && acc.push(curr['cidr']); + + return acc; + }, []); + } + + resvOverlap = isSubnetOverlap(newCidr, openResv); + vnetOverlap = isSubnetOverlap(newCidr, blockNetworks); + extOverlap = isSubnetOverlap(newCidr, extNetworks); + } + + setExtCidr({ + value: newCidr, + error: (cidrError || !cidrInBlock || resvOverlap || vnetOverlap || extOverlap) + }); + } + + const unchanged = React.useMemo(() => { + if(selectedExternal) { + return ( + extName.value === selectedExternal.name && + extDesc.value === selectedExternal.desc && + extCidr.value === selectedExternal.cidr + ); + } else { + return true; + } + }, [selectedExternal, extName, extDesc, extCidr]); + + const hasError = React.useMemo(() => { + var emptyCheck = false; + var errorCheck = false; + + errorCheck = (extName.error || extDesc.error || extCidr.error); + emptyCheck = (extName.value.length === 0 || extDesc.value.length === 0 || extCidr.value.length === 0); + + return (errorCheck || emptyCheck); + }, [extName, extDesc, extCidr]); + + React.useEffect(() => { + if (selectedExternal) { + setExtName({ value: selectedExternal.name, error: false }); + setExtDesc({ value: selectedExternal.desc, error: false }); + setExtCidr({ value: selectedExternal.cidr, error: false }); + } else { + handleClose(); + + setExtName({ value: "", error: false }); + setExtDesc({ value: "", error: false }); + setExtCidr({ value: "", error: false }); + } + }, [selectedExternal, handleClose]); + + return ( +
+ + + Edit External Network + + + + + - Network name must be unique +
- Max of 64 characters +
- Can contain alphnumerics +
- Can contain underscore, hypen and period +
- Cannot start/end with underscore, hypen or period + + } + > + onNameChange(event)} + inputProps={{ spellCheck: false }} + sx={{ width: "80%" }} + /> +
+ + - Max of 128 characters +
- Can contain alphnumerics +
- Can contain spaces +
- Can contain underscore, hypen, slash and period +
- Cannot start/end with underscore, hypen, slash or period + + } + > + onDescChange(event)} + inputProps={{ spellCheck: false }} + sx={{ width: "80%" }} + /> +
+ + - Must be in valid CIDR notation format +
    - Example: 1.2.3.4/5 +
- Cannot overlap existing subnets + + } + > + 0 && extCidr.error)} + margin="dense" + id="name" + label="CIDR" + type="cidr" + variant="standard" + value={extCidr.value} + onChange={(event) => onCidrChange(event)} + inputProps={{ spellCheck: false }} + sx={{ width: "80%" }} + /> +
+
+
+ + + + Update + + +
+
+ ); +} diff --git a/ui/src/features/configure/externals/subnets/subnets.jsx b/ui/src/features/configure/externals/subnets/subnets.jsx index 10a2545..a647a36 100644 --- a/ui/src/features/configure/externals/subnets/subnets.jsx +++ b/ui/src/features/configure/externals/subnets/subnets.jsx @@ -29,17 +29,19 @@ import { TaskAltOutlined, CancelOutlined, AddOutlined, - // EditOutlined, + EditOutlined, DeleteOutline, EditNoteOutlined } from "@mui/icons-material"; import { selectViewSetting, - updateMeAsync + updateMeAsync, + getAdminStatus } from "../../../ipam/ipamSlice"; import AddExtSubnet from "./utils/addSubnet"; +import EditExtSubnet from "./utils/editSubnet"; import DeleteExtSubnet from "./utils/deleteSubnet"; import ManageExtEndpoints from "./utils/manageEndpoints"; @@ -59,6 +61,7 @@ function HeaderMenu(props) { selectedExternal, selectedSubnet, setAddExtSubOpen, + setEditExtSubOpen, setDelExtSubOpen, setManExtEndOpen, saving, @@ -72,6 +75,7 @@ function HeaderMenu(props) { const menuRef = React.useRef(null); + const isAdmin = useSelector(getAdminStatus); const viewSetting = useSelector(state => selectViewSetting(state, setting)); const onClick = () => { @@ -83,6 +87,11 @@ function HeaderMenu(props) { setMenuOpen(false); } + const onEditExtSub = () => { + setEditExtSubOpen(true); + setMenuOpen(false); + } + const onDelExtSub = () => { setDelExtSubOpen(true); setMenuOpen(false); @@ -183,25 +192,25 @@ function HeaderMenu(props) { > Add Subnet - {/* console.log("EDIT!")} - disabled={ true } + Edit Subnet - */} + @@ -270,9 +279,11 @@ const Subnets = (props) => { const [columnSortState, setColumnSortState] = React.useState({}); const [addExtSubOpen, setAddExtSubOpen] = React.useState(false); + const [editExtSubOpen, setEditExtSubOpen] = React.useState(false); const [delExtSubOpen, setDelExtSubOpen] = React.useState(false); const [manExtEndOpen, setManExtEndOpen] = React.useState(false); + const isAdmin = useSelector(getAdminStatus); const viewSetting = useSelector(state => selectViewSetting(state, 'extsubnets')); const saveTimer = React.useRef(); @@ -497,22 +508,35 @@ const Subnets = (props) => { return ( - setAddExtSubOpen(false)} - space={selectedSpace ? selectedSpace.name : null} - block={selectedBlock ? selectedBlock.name : null} - external={selectedExternal ? selectedExternal : null} - subnets={subnets} - /> - setDelExtSubOpen(false)} - space={selectedSpace ? selectedSpace.name : null} - block={selectedBlock ? selectedBlock.name : null} - external={selectedExternal ? selectedExternal.name : null} - subnet={selectedSubnet ? selectedSubnet.name : null} - /> + { !isAdmin && + + setAddExtSubOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + block={selectedBlock ? selectedBlock.name : null} + external={selectedExternal ? selectedExternal : null} + subnets={subnets} + /> + setEditExtSubOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + block={selectedBlock ? selectedBlock.name : null} + external={selectedExternal ? selectedExternal : null} + subnets={subnets} + selectedSubnet={selectedSubnet} + /> + setDelExtSubOpen(false)} + space={selectedSpace ? selectedSpace.name : null} + block={selectedBlock ? selectedBlock.name : null} + external={selectedExternal ? selectedExternal.name : null} + subnet={selectedSubnet ? selectedSubnet.name : null} + /> + + } setManExtEndOpen(false)} @@ -521,7 +545,7 @@ const Subnets = (props) => { external={selectedExternal ? selectedExternal.name : null} subnet={selectedSubnet ? selectedSubnet : null} /> - + diff --git a/ui/src/features/configure/externals/subnets/utils/addSubnet.jsx b/ui/src/features/configure/externals/subnets/utils/addSubnet.jsx index 3a4d4a8..88fd142 100644 --- a/ui/src/features/configure/externals/subnets/utils/addSubnet.jsx +++ b/ui/src/features/configure/externals/subnets/utils/addSubnet.jsx @@ -36,7 +36,6 @@ import { CIDR_REGEX, cidrMasks } from "../../../../../global/globals"; -import { Spellcheck } from "@mui/icons-material"; function DraggablePaper(props) { const nodeRef = React.useRef(null); diff --git a/ui/src/features/configure/externals/subnets/utils/editSubnet.jsx b/ui/src/features/configure/externals/subnets/utils/editSubnet.jsx new file mode 100644 index 0000000..952b399 --- /dev/null +++ b/ui/src/features/configure/externals/subnets/utils/editSubnet.jsx @@ -0,0 +1,335 @@ +import * as React from "react"; +import { useDispatch } from "react-redux"; + +import { useSnackbar } from "notistack"; + +import Draggable from "react-draggable"; + +import { + Box, + Button, + Tooltip, + TextField, + Dialog, + DialogTitle, + DialogActions, + DialogContent, + Paper +} from "@mui/material"; + +import LoadingButton from "@mui/lab/LoadingButton"; + +import { + updateBlockExtSubnetAsync +} from "../../../../ipam/ipamSlice"; + +import { + isSubnetOf, + isSubnetOverlap +} from "../../../../tools/planner/utils/iputils"; + +import { + EXTSUBNET_NAME_REGEX, + EXTSUBNET_DESC_REGEX, + CIDR_REGEX +} from "../../../../../global/globals"; + +function DraggablePaper(props) { + const nodeRef = React.useRef(null); + + return ( + + + + ); +} + +export default function EditExtSubnet(props) { + const { open, handleClose, space, block, external, subnets, selectedSubnet } = props; + + const { enqueueSnackbar } = useSnackbar(); + + const [subName, setSubName] = React.useState({ value: "", error: false }); + const [subDesc, setSubDesc] = React.useState({ value: "", error: false }); + const [subCidr, setSubCidr] = React.useState({ value: "", error: false }); + + const [sending, setSending] = React.useState(false); + + const dispatch = useDispatch(); + + function onCancel() { + handleClose(); + + if(selectedSubnet) { + setSubName({ value: selectedSubnet.name, error: false }); + setSubDesc({ value: selectedSubnet.desc, error: false }); + setSubCidr({ value: selectedSubnet.cidr, error: false }); + } else { + setSubName({ value: "", error: false }); + setSubDesc({ value: "", error: false }); + setSubCidr({ value: "", error: false }); + } + } + + function onSubmit() { + var body = [ + { + op: "replace", + path: "/name", + value: subName.value + }, + { + op: "replace", + path: "/desc", + value: subDesc.value + }, + { + op: "replace", + path: "/cidr", + value: subCidr.value + } + ]; + + (async () => { + try { + setSending(true); + await dispatch(updateBlockExtSubnetAsync({ space: space, block: block, external: external.name, subnet: selectedSubnet.name, body: body })); + enqueueSnackbar("Successfully updated External Subnet", { variant: "success" }); + onCancel(); + } catch (e) { + console.log("ERROR"); + console.log("------------------"); + console.log(e); + console.log("------------------"); + enqueueSnackbar(e.message, { variant: "error" }); + } finally { + setSending(false); + } + })(); + } + + function onNameChange(event) { + const newName = event.target.value; + + if(subnets) { + const regex = new RegExp( + EXTSUBNET_NAME_REGEX + ); + + const nameError = newName ? !regex.test(newName) : false; + const nameExists = subnets?.reduce((acc, curr) => { + curr['name'] !== selectedSubnet['name'] && acc.push(curr['name'].toLowerCase()); + + return acc; + }, []).includes(newName.toLowerCase()); + + setSubName({ + value: newName, + error: (nameError || nameExists) + }); + } + } + + function onDescChange(event) { + const newDesc = event.target.value; + + const regex = new RegExp( + EXTSUBNET_DESC_REGEX + ); + + setSubDesc({ + value: newDesc, + error: (newDesc ? !regex.test(newDesc) : false) + }); + } + + function onCidrChange(event) { + const newCidr = event.target.value; + + const regex = new RegExp( + CIDR_REGEX + ); + + const cidrError = newCidr ? !regex.test(newCidr) : false; + + var extSubnets = []; + + var cidrInBlock = false; + var subOverlap = true; + + if(!cidrError && newCidr.length > 0) { + cidrInBlock = isSubnetOf(newCidr, external.cidr); + + if(subnets) { + extSubnets = subnets?.reduce((acc, curr) => { + curr['name'] !== selectedSubnet['name'] && acc.push(curr['cidr']); + + return acc; + }, []); + } + + subOverlap = isSubnetOverlap(newCidr, extSubnets); + } + + setSubCidr({ + value: newCidr, + error: (cidrError || !cidrInBlock || subOverlap) + }); + } + + const unchanged = React.useMemo(() => { + if(selectedSubnet) { + return ( + subName.value === selectedSubnet.name && + subDesc.value === selectedSubnet.desc && + subCidr.value === selectedSubnet.cidr + ); + } else { + return true; + } + }, [selectedSubnet, subName, subDesc, subCidr]); + + const hasError = React.useMemo(() => { + var emptyCheck = false; + var errorCheck = false; + + errorCheck = (subName.error || subDesc.error || subCidr.error); + emptyCheck = (subName.value.length === 0 || subDesc.value.length === 0 || subCidr.value.length === 0); + + return (errorCheck || emptyCheck); + }, [subName, subDesc, subCidr]); + + React.useEffect(() => { + if (selectedSubnet) { + setSubName({ value: selectedSubnet.name, error: false }); + setSubDesc({ value: selectedSubnet.desc, error: false }); + setSubCidr({ value: selectedSubnet.cidr, error: false }); + } else { + handleClose(); + + setSubName({ value: "", error: false }); + setSubDesc({ value: "", error: false }); + setSubCidr({ value: "", error: false }); + } + }, [selectedSubnet, handleClose]); + + return ( +
+ + + Edit External Subnet + + + + + - Subnet name must be unique +
- Max of 64 characters +
- Can contain alphnumerics +
- Can contain underscore, hypen and period +
- Cannot start/end with underscore, hypen or period + + } + > + onNameChange(event)} + inputProps={{ spellCheck: false }} + sx={{width: "80%" }} + /> +
+ + - Max of 128 characters +
- Can contain alphnumerics +
- Can contain spaces +
- Can contain underscore, hypen, slash and period +
- Cannot start/end with underscore, hypen, slash or period + + } + > + onDescChange(event)} + inputProps={{ spellCheck: false }} + sx={{ width: "80%" }} + /> +
+ + - Must be in valid CIDR notation format +
    - Example: 1.2.3.4/5 +
- Cannot overlap existing subnets + + } + > + 0 && subCidr.error)} + margin="dense" + id="name" + label="CIDR" + type="cidr" + variant="standard" + value={subCidr.value} + onChange={(event) => onCidrChange(event)} + inputProps={{ spellCheck: false }} + sx={{ width: "80%" }} + /> +
+
+
+ + + + Update + + +
+
+ ); +} diff --git a/ui/src/features/configure/externals/subnets/utils/manageEndpoints.jsx b/ui/src/features/configure/externals/subnets/utils/manageEndpoints.jsx index d8a4575..816991f 100644 --- a/ui/src/features/configure/externals/subnets/utils/manageEndpoints.jsx +++ b/ui/src/features/configure/externals/subnets/utils/manageEndpoints.jsx @@ -2,7 +2,7 @@ import * as React from "react"; import { useSelector, useDispatch } from "react-redux"; import { styled } from "@mui/material/styles"; -import { isEmpty, isEqual, pickBy, orderBy, cloneDeep } from "lodash"; +import { omit, isEmpty, isEqual, pickBy, orderBy, cloneDeep } from "lodash"; import { useSnackbar } from "notistack"; @@ -14,6 +14,8 @@ import Draggable from "react-draggable"; import { useTheme } from "@mui/material/styles"; +import md5 from "md5"; + import { Box, Button, @@ -43,7 +45,9 @@ import { TaskAltOutlined, CancelOutlined, PlaylistAddOutlined, - HighlightOff, + PlaylistAddCheckOutlined, + PlaylistRemoveOutlined, + // HighlightOff, InfoOutlined } from "@mui/icons-material"; @@ -52,11 +56,13 @@ import LoadingButton from "@mui/lab/LoadingButton"; import { replaceBlockExtSubnetEndpointsAsync, selectViewSetting, - updateMeAsync + updateMeAsync, + getAdminStatus } from "../../../../ipam/ipamSlice"; import { - expandCIDR + expandCIDR, + getSubnetSize } from "../../../../tools/planner/utils/iputils"; import { @@ -87,7 +93,7 @@ const gridStyle = { function RenderDelete(props) { const { value } = props; - const { endpoints, setAdded, deleted, setDeleted, selectionModel } = React.useContext(EndpointContext); + const { setChanges, selectionModel } = React.useContext(EndpointContext); const flexCenter = { display: "flex", @@ -108,14 +114,18 @@ function RenderDelete(props) { disableTouchRipple disableRipple onClick={() => { - if(endpoints.find(e => e.name === value.name) && !deleted.includes(value.name)) { - setDeleted(prev => [...prev, value.name]); - } else { - setAdded(prev => prev.filter(e => e.name !== value.name)); - } + var endpointDetails = cloneDeep(value); + + endpointDetails['op'] = "delete"; + + setChanges(prev => [ + ...prev, + endpointDetails + ]); }} > - + {/* */} + @@ -284,8 +294,7 @@ export default function ManageExtEndpoints(props) { const [sendResults, setSendResults] = React.useState(null); const [endpoints, setEndpoints] = React.useState(null); const [addressOptions, setAddressOptions] = React.useState([]); - const [added, setAdded] = React.useState([]); - const [deleted, setDeleted] = React.useState([]); + const [changes, setChanges] = React.useState([]); const [gridData, setGridData] = React.useState(null); const [sending, setSending] = React.useState(false); const [selectionModel, setSelectionModel] = React.useState({}); @@ -300,6 +309,7 @@ export default function ManageExtEndpoints(props) { const [endAddrInput, setEndAddrInput] = React.useState(""); const [endAddr, setEndAddr] = React.useState(null); + const isAdmin = useSelector(getAdminStatus); const viewSetting = useSelector(state => selectViewSetting(state, 'extendpoints')); const dispatch = useDispatch(); @@ -331,6 +341,31 @@ export default function ManageExtEndpoints(props) { setSelectionModel(prevState => { if(!prevState.hasOwnProperty(id)) { newSelectionModel[id] = data; + + setEndName({ value: data.name, error: false }); + setEndDesc({ value: data.desc, error: false }); + setEndAddrInput(data.ip); + setEndAddr(data.ip); + + const endpointAddresses = endpoints.map(e => { + if (data.ip !== e.ip) { + return e.ip; + } + }); + + const newAddressOptions = expandCIDR(subnet.cidr).slice(1,-1).filter(addr => !endpointAddresses.includes(addr)); + + setAddressOptions(newAddressOptions); + } else { + setEndName({ value: "", error: true }); + setEndDesc({ value: "", error: true }); + setEndAddrInput(""); + setEndAddr(null); + + const endpointAddresses = endpoints.map(e => e.ip); + const newAddressOptions = expandCIDR(subnet.cidr).slice(1,-1).filter(addr => !endpointAddresses.includes(addr)); + + setAddressOptions(["", ...newAddressOptions]); } return newSelectionModel; @@ -482,14 +517,43 @@ export default function ManageExtEndpoints(props) { function onAddExternal() { if(!hasError) { - setAdded(prev => [ - ...prev, - { - name: endName.value, - desc: endDesc.value, - ip: endAddr + var endpointDetails = { + name: endName.value, + desc: endDesc.value, + ip: endAddr + }; + + endpointDetails['id'] = md5(JSON.stringify(endpointDetails)); + + if (Object.keys(selectionModel).length !== 0) { + const updates = { + op: "update", + old: Object.values(selectionModel)[0], + new: endpointDetails + } + + setChanges(prev => [ + ...prev, + updates + ]); + } else { + const numEndpoints = endpoints.length; + const numAdditions = changes.filter(change => change.op === "add").length; + const numDeletions = changes.filter(change => change.op === "delete").length; + const subnetSize = getSubnetSize(subnet.cidr) - 2; + + if (((numEndpoints + numAdditions) - numDeletions) >= subnetSize) { + enqueueSnackbar(`Number of endpoints cannot exceed subnet size of ${subnetSize}`, { variant: "error" }); + return; } - ]); + + endpointDetails['op'] = "add"; + + setChanges(prev => [ + ...prev, + endpointDetails + ]); + } setEndName({ value: "", error: true }); setEndDesc({ value: "", error: true }); @@ -533,15 +597,15 @@ export default function ManageExtEndpoints(props) { const onCancel = React.useCallback(() => { if (open) { - setAdded([]); - setDeleted([]); + handleClose(); + + setSelectionModel({}); + setChanges([]); setEndName({ value: "", error: true }); setEndDesc({ value: "", error: true }); setEndAddrInput(""); setEndAddr(null); - - handleClose(); } }, [open, handleClose]); @@ -561,7 +625,17 @@ export default function ManageExtEndpoints(props) { ); const nameError = newName ? !regex.test(newName) : false; - const nameExists = endpoints.map(e => e.name.toLowerCase()).includes(newName.toLowerCase()); + const nameExists = endpoints?.reduce((acc, curr) => { + if(Object.keys(selectionModel).length !== 0) { + if (curr['name'].toLowerCase() !== Object.values(selectionModel)[0].name.toLowerCase()) { + acc.push(curr['name'].toLowerCase()); + } + } else { + acc.push(curr['name'].toLowerCase()); + } + + return acc; + }, []).includes(newName.toLowerCase()); setEndName({ value: newName, @@ -594,17 +668,41 @@ export default function ManageExtEndpoints(props) { if(subnet) { var newEndpoints = cloneDeep(subnet['endpoints']); - newEndpoints = newEndpoints.filter(e => !deleted.includes(e.name)); - newEndpoints = newEndpoints.concat(added); - - const newData = newEndpoints.reduce((acc, curr) => { - curr['id'] = `${subnet}@${curr.name}}` + var newData = newEndpoints.reduce((acc, curr) => { + // curr['id'] = `${subnet}@${curr.name}}`; + curr['id'] = md5(JSON.stringify(curr)); acc.push(curr); return acc; }, []); + changes.forEach(change => { + switch(change.op) { + case "add": { + newData.push(omit(change, 'op')); + + break; + } + case "update": { + const index = newData.findIndex(e => e.name === change.old.name); + + if (index !== -1) { + newData[index] = change.new; + } + + break; + } + case "delete": { + newData = newData.filter(e => e.name !== change.name); + + break; + } + default: + break; + } + }); + const endpointAddresses = newData.map(e => e.ip); const newAddressOptions = expandCIDR(subnet.cidr).slice(1,-1).filter(addr => !endpointAddresses.includes(addr)); @@ -613,10 +711,10 @@ export default function ManageExtEndpoints(props) { } else { onCancel(); } - }, [subnet, added, deleted, onCancel]); + }, [subnet, changes, onCancel]); return ( - + Define the Endpoints below which should be associated with the Subnet '{subnet && subnet.name}' + { isAdmin && + - Add New Endpoint + { + Object.keys(selectionModel).length !== 0 ? + "Edit Existing Endpoint" : + "Add New Endpoint" + } - + { + Object.keys(selectionModel).length !== 0 ? + : + + }
+
+ } onClick(rowData.data)} + onRowClick={(rowData) => { isAdmin && onClick(rowData.data)}} onCellDoubleClick={onCellDoubleClick} selected={selectionModel} style={gridStyle} diff --git a/ui/src/features/ipam/ipamAPI.jsx b/ui/src/features/ipam/ipamAPI.jsx index 4b6a825..84498d8 100644 --- a/ui/src/features/ipam/ipamAPI.jsx +++ b/ui/src/features/ipam/ipamAPI.jsx @@ -149,6 +149,12 @@ export function createBlockExternal(space, block, body) { return api.post(url, body); } +export function updateBlockExternal(space, block, external, body) { + const url = new URL(`${ENGINE_URL}/api/spaces/${space}/blocks/${block}/externals/${external}`); + + return api.patch(url, body); +} + export function deleteBlockExternal(space, block, external, force) { const url = new URL(`${ENGINE_URL}/api/spaces/${space}/blocks/${block}/externals/${external}`); var urlParams = url.searchParams; @@ -164,6 +170,12 @@ export function createBlockExtSubnet(space, block, external, body) { return api.post(url, body); } +export function updateBlockExtSubnet(space, block, external, subnet, body) { + const url = new URL(`${ENGINE_URL}/api/spaces/${space}/blocks/${block}/externals/${external}/subnets/${subnet}`); + + return api.patch(url, body); +} + export function deleteBlockExtSubnet(space, block, external, subnet, force) { const url = new URL(`${ENGINE_URL}/api/spaces/${space}/blocks/${block}/externals/${external}/subnets/${subnet}`); var urlParams = url.searchParams; diff --git a/ui/src/features/ipam/ipamSlice.jsx b/ui/src/features/ipam/ipamSlice.jsx index 1c6c17a..c47d43c 100644 --- a/ui/src/features/ipam/ipamSlice.jsx +++ b/ui/src/features/ipam/ipamSlice.jsx @@ -13,8 +13,10 @@ import { updateBlock, deleteBlock, createBlockExternal, + updateBlockExternal, deleteBlockExternal, createBlockExtSubnet, + updateBlockExtSubnet, deleteBlockExtSubnet, replaceBlockExtSubnetEndpoints, createBlockResv, @@ -178,6 +180,19 @@ export const createBlockExternalAsync = createAsyncThunk( } ); +export const updateBlockExternalAsync = createAsyncThunk( + 'ipam/updateBlockExternal', + async (args, { rejectWithValue }) => { + try { + const response = await updateBlockExternal(args.space, args.block, args.external, args.body); + + return response; + } catch (err) { + return rejectWithValue(err); + } + } +); + export const deleteBlockExternalAsync = createAsyncThunk( 'ipam/deleteBlockExternal', async (args, { rejectWithValue }) => { @@ -204,6 +219,19 @@ export const createBlockExtSubnetAsync = createAsyncThunk( } ); +export const updateBlockExtSubnetAsync = createAsyncThunk( + 'ipam/updateBlockExtSubnet', + async (args, { rejectWithValue }) => { + try { + const response = await updateBlockExtSubnet(args.space, args.block, args.external, args.subnet, args.body); + + return response; + } catch (err) { + return rejectWithValue(err); + } + } +); + export const deleteBlockExtSubnetAsync = createAsyncThunk( 'ipam/deleteBlockExtSubnet', async (args, { rejectWithValue }) => { @@ -565,6 +593,31 @@ export const ipamSlice = createSlice({ // SnackbarUtils.error(`Error fetching user settings (${action.error.message})`); throw action.payload; }) + .addCase(updateBlockExternalAsync.fulfilled, (state, action) => { + const spaceName = action.meta.arg.space; + const blockName = action.meta.arg.block; + const externalName = action.meta.arg.external; + const updatedExternal = action.payload; + const spaceIndex = state.spaces.findIndex((x) => x.name === spaceName); + + if (spaceIndex > -1) { + const blockIndex = state.spaces[spaceIndex].blocks.findIndex((x) => x.name === blockName); + + if(blockIndex > -1) { + const externalIndex = state.spaces[spaceIndex].blocks[blockIndex].externals.findIndex((x) => x.name === externalName); + + if(externalIndex > -1) { + state.spaces[spaceIndex].blocks[blockIndex].externals[externalIndex] = merge(state.spaces[spaceIndex].blocks[blockIndex].externals[externalIndex], updatedExternal); + } + } + } + }) + .addCase(updateBlockExternalAsync.rejected, (state, action) => { + console.log("updateBlockExternalAsync Rejected"); + console.log(action); + // SnackbarUtils.error(`Error fetching user settings (${action.error.message})`); + throw action.payload; + }) .addCase(deleteBlockExternalAsync.fulfilled, (state, action) => { const spaceName = action.meta.arg.space; const blockName = action.meta.arg.block; @@ -604,6 +657,36 @@ export const ipamSlice = createSlice({ // SnackbarUtils.error(`Error fetching user settings (${action.error.message})`); throw action.payload; }) + .addCase(updateBlockExtSubnetAsync.fulfilled, (state, action) => { + const spaceName = action.meta.arg.space; + const blockName = action.meta.arg.block; + const externalName = action.meta.arg.external; + const subnetName = action.meta.arg.subnet; + const updatedSubnet = action.payload; + const spaceIndex = state.spaces.findIndex((x) => x.name === spaceName); + + if (spaceIndex > -1) { + const blockIndex = state.spaces[spaceIndex].blocks.findIndex((x) => x.name === blockName); + + if(blockIndex > -1) { + const externalIndex = state.spaces[spaceIndex].blocks[blockIndex].externals.findIndex((x) => x.name === externalName); + + if(externalIndex > -1) { + const subnetIndex = state.spaces[spaceIndex].blocks[blockIndex].externals[externalIndex].subnets.findIndex((x) => x.name === subnetName); + + if(subnetIndex > -1) { + state.spaces[spaceIndex].blocks[blockIndex].externals[externalIndex].subnets[subnetIndex] = merge(state.spaces[spaceIndex].blocks[blockIndex].externals[externalIndex].subnets[subnetIndex], updatedSubnet); + } + } + } + } + }) + .addCase(updateBlockExtSubnetAsync.rejected, (state, action) => { + console.log("updateBlockExtSubnetAsync Rejected"); + console.log(action); + // SnackbarUtils.error(`Error fetching user settings (${action.error.message})`); + throw action.payload; + }) .addCase(deleteBlockExtSubnetAsync.fulfilled, (state, action) => { const spaceName = action.meta.arg.space; const blockName = action.meta.arg.block; diff --git a/ui/src/features/tools/planner/utils/iputils.jsx b/ui/src/features/tools/planner/utils/iputils.jsx index c1c6b81..a80ceea 100644 --- a/ui/src/features/tools/planner/utils/iputils.jsx +++ b/ui/src/features/tools/planner/utils/iputils.jsx @@ -23,6 +23,13 @@ function getIpRangeForSubnet(cidr) { return results; } +export function getSubnetSize(cidr) { + var mask = parseInt(cidr.split('/')[1], 10); + var size = Math.pow(2, (32 - mask)); + + return size; +} + export function isSubnetOverlap(subnetCIDR, existingSubnetCIDR) { var ipRangeforCurrent = getIpRangeForSubnet(subnetCIDR);