diff --git a/frontend/desktop/src/components/desktop_content/index.tsx b/frontend/desktop/src/components/desktop_content/index.tsx index f97ef3f39..1553a367d 100644 --- a/frontend/desktop/src/components/desktop_content/index.tsx +++ b/frontend/desktop/src/components/desktop_content/index.tsx @@ -3,7 +3,7 @@ import MoreButton from '@/components/more_button'; import UserMenu from '@/components/user_menu'; import useDriver from '@/hooks/useDriver'; import useAppStore from '@/stores/app'; -import { TApp } from '@/types'; +import { TApp, WindowSize } from '@/types'; import { Box, Flex, Grid, GridItem, Image, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import dynamic from 'next/dynamic'; @@ -54,17 +54,19 @@ export default function DesktopContent(props: any) { appKey, query = {}, messageData = {}, - pathname = '/' + pathname = '/', + appSize = 'maximize' }: { appKey: string; query?: Record; messageData?: Record; pathname: string; + appSize?: WindowSize; }) => { const app = apps.find((item) => item.key === appKey); const runningApp = runningInfo.find((item) => item.key === appKey); if (!app) return; - openApp(app, { query, pathname }); + openApp(app, { query, pathname, appSize }); if (runningApp) { setToHighestLayerById(runningApp.pid); } diff --git a/frontend/packages/ui/src/components/RangeInput/index.tsx b/frontend/packages/ui/src/components/RangeInput/index.tsx index c0e2a156b..db018a64c 100644 --- a/frontend/packages/ui/src/components/RangeInput/index.tsx +++ b/frontend/packages/ui/src/components/RangeInput/index.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { HStack, Input, useNumberInput, IconButton } from '@chakra-ui/react'; +import { HStack, Input, useNumberInput, IconButton, InputProps } from '@chakra-ui/react'; import { AddIcon, MinusIcon } from '@chakra-ui/icons'; import { MyTooltip } from '../MyTooltip'; @@ -11,7 +11,9 @@ export const RangeInput = ({ max = Infinity, value, setVal, - hoverText + hoverText, + inputStyle, + isDisabled = false }: { w?: number; step?: number; @@ -21,6 +23,8 @@ export const RangeInput = ({ value: number | ''; setVal: (val: number) => void; hoverText?: string; + inputStyle?: InputProps; + isDisabled?: boolean; }) => { const { getInputProps, getIncrementButtonProps, getDecrementButtonProps } = useNumberInput({ focusInputOnChange: false, @@ -68,6 +72,7 @@ export const RangeInput = ({ textAlign={'center'} fontSize={'lg'} fontWeight={'bold'} + {...inputStyle} {...input} /> void; + isInvalid?: boolean; } const MySelect = ( - { placeholder, value, width = 'auto', height = '30px', list, onchange, ...props }: Props, + { + placeholder, + value, + width = 'auto', + height = '30px', + list, + onchange, + isInvalid, + ...props + }: Props, selectRef: any ) => { const ref = useRef(null); @@ -75,7 +85,8 @@ const MySelect = ( bg: '#FFF' } : { - bg: '#F7F8FA' + bg: '#F7F8FA', + borderColor: isInvalid ? 'red' : '' })} {...props} > diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 2fb575189..03c1d0681 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 2.2.0(@chakra-ui/react@2.8.2)(@emotion/react@11.11.1)(next@13.5.6)(react@18.2.0) '@chakra-ui/react': specifier: ^2.8.1 - version: 2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) + version: 2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) '@chakra-ui/system': specifier: ^2.6.1 version: 2.6.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) @@ -28,7 +28,7 @@ importers: version: 11.11.1(@types/react@18.2.37)(react@18.2.0) '@emotion/styled': specifier: ^11.11.0 - version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.37)(react@18.2.0) + version: 11.11.0(@emotion/react@11.11.1)(react@18.2.0) framer-motion: specifier: ^10.16.4 version: 10.16.5(react-dom@18.2.0)(react@18.2.0) @@ -514,7 +514,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -595,6 +595,187 @@ importers: specifier: ^4.9.1 version: 4.10.1 + providers/cloudserver: + dependencies: + '@chakra-ui/anatomy': + specifier: ^2.2.1 + version: 2.2.2 + '@chakra-ui/icons': + specifier: ^2.1.1 + version: 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/next-js': + specifier: ^2.1.5 + version: 2.2.0(@chakra-ui/react@2.8.2)(@emotion/react@11.11.1)(next@13.1.6)(react@18.2.0) + '@chakra-ui/react': + specifier: ^2.8.1 + version: 2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/system': + specifier: ^2.6.1 + version: 2.6.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) + '@emotion/react': + specifier: ^11.11.1 + version: 11.11.1(@types/react@18.2.37)(react@18.2.0) + '@emotion/styled': + specifier: ^11.11.0 + version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.37)(react@18.2.0) + '@kubernetes/client-node': + specifier: ^0.18.1 + version: 0.18.1 + '@next/font': + specifier: 13.1.6 + version: 13.1.6 + '@sealos/ui': + specifier: workspace:^ + version: link:../../packages/ui + '@tanstack/react-query': + specifier: ^4.35.3 + version: 4.36.1(react-dom@18.2.0)(react@18.2.0) + '@types/multer': + specifier: ^1.4.10 + version: 1.4.11 + ansi_up: + specifier: ^5.2.1 + version: 5.2.1 + axios: + specifier: ^1.5.1 + version: 1.6.2 + date-fns: + specifier: ^2.30.0 + version: 2.30.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 + decimal.js: + specifier: ^10.4.3 + version: 10.4.3 + echarts: + specifier: ^5.4.3 + version: 5.4.3 + framer-motion: + specifier: ^10.16.4 + version: 10.16.5(react-dom@18.2.0)(react@18.2.0) + i18next: + specifier: ^22.5.1 + version: 22.5.1 + immer: + specifier: ^9.0.21 + version: 9.0.21 + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 + jszip: + specifier: ^3.10.1 + version: 3.10.1 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + minio: + specifier: ^7.1.3 + version: 7.1.3 + mongodb: + specifier: ^5.9.0 + version: 5.9.1 + multer: + specifier: 1.4.5-lts.1 + version: 1.4.5-lts.1 + nanoid: + specifier: ^4.0.2 + version: 4.0.2 + next: + specifier: 13.1.6 + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + next-i18next: + specifier: ^13.3.0 + version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + prettier: + specifier: ^2.8.8 + version: 2.8.8 + react: + specifier: 18.2.0 + version: 18.2.0 + react-day-picker: + specifier: ^8.8.2 + version: 8.9.1(date-fns@2.30.0)(react@18.2.0) + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-hook-form: + specifier: ^7.46.2 + version: 7.48.2(react@18.2.0) + react-i18next: + specifier: ^12.3.1 + version: 12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) + react-markdown: + specifier: ^8.0.7 + version: 8.0.7(@types/react@18.2.37)(react@18.2.0) + react-syntax-highlighter: + specifier: ^15.5.0 + version: 15.5.0(react@18.2.0) + sass: + specifier: ^1.68.0 + version: 1.69.5 + sealos-desktop-sdk: + specifier: workspace:* + version: link:../../packages/client-sdk + typescript: + specifier: ^5.2.2 + version: 5.2.2 + zustand: + specifier: ^4.4.1 + version: 4.4.7(@types/react@18.2.37)(immer@9.0.21)(react@18.2.0) + devDependencies: + '@svgr/webpack': + specifier: ^6.5.1 + version: 6.5.1 + '@types/js-cookie': + specifier: ^3.0.4 + version: 3.0.6 + '@types/js-yaml': + specifier: ^4.0.6 + version: 4.0.9 + '@types/jsonwebtoken': + specifier: ^9.0.3 + version: 9.0.5 + '@types/lodash': + specifier: ^4.14.199 + version: 4.14.202 + '@types/node': + specifier: 18.13.0 + version: 18.13.0 + '@types/nprogress': + specifier: ^0.2.1 + version: 0.2.3 + '@types/react': + specifier: 18.2.37 + version: 18.2.37 + '@types/react-dom': + specifier: 18.0.10 + version: 18.0.10 + '@types/react-syntax-highlighter': + specifier: ^15.5.7 + version: 15.5.10 + eslint: + specifier: 8.33.0 + version: 8.33.0 + eslint-config-next: + specifier: 13.1.6 + version: 13.1.6(eslint@8.33.0)(typescript@5.2.2) + svg-inline-loader: + specifier: ^0.8.2 + version: 0.8.2 + webpack-bundle-analyzer: + specifier: ^4.9.1 + version: 4.10.1 + providers/costcenter: dependencies: '@alicloud/dysmsapi20170525': @@ -686,7 +867,7 @@ importers: version: 5.9.1 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -831,7 +1012,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -1000,7 +1181,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -1163,7 +1344,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -1480,7 +1661,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -1800,7 +1981,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -2845,7 +3026,7 @@ packages: /@antv/util@2.0.17: resolution: {integrity: sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==} dependencies: - csstype: 3.1.2 + csstype: 3.1.3 tslib: 2.6.2 dev: false @@ -2891,7 +3072,7 @@ packages: /@antv/x6@1.35.0: resolution: {integrity: sha512-OwpGQelMc/zEOfJwaAvkJQ88JYEbyGKYOjI5RhHXTvGj5NTkZgOnNTzVx0RzcZRfUGgjZ7YPYprSKsxa9+/gfw==} dependencies: - csstype: 3.1.2 + csstype: 3.1.3 jquery: 3.7.1 jquery-mousewheel: 3.1.13 lodash-es: 4.17.21 @@ -3638,28 +3819,6 @@ packages: resolution: {integrity: sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==} engines: {node: '>=6.9.0'} - /@babel/core@7.23.3: - resolution: {integrity: sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.4 - '@babel/generator': 7.23.4 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) - '@babel/helpers': 7.23.4 - '@babel/parser': 7.23.4 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.4 - '@babel/types': 7.23.4 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - /@babel/core@7.23.5: resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==} engines: {node: '>=6.9.0'} @@ -3690,6 +3849,7 @@ packages: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.20 jsesc: 2.5.2 + dev: true /@babel/generator@7.23.5: resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} @@ -3722,40 +3882,40 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.3): + /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.5): resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.3): + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.5): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 - /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.3): + /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.5): resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4 @@ -3785,7 +3945,7 @@ packages: resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.23.5 /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} @@ -3793,19 +3953,6 @@ packages: dependencies: '@babel/types': 7.23.4 - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.3): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} engines: {node: '>=6.9.0'} @@ -3829,24 +3976,24 @@ packages: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} engines: {node: '>=6.9.0'} - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.3): + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.5): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.20 - /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.3): + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5): resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 @@ -3889,16 +4036,6 @@ packages: '@babel/template': 7.22.15 '@babel/types': 7.23.4 - /@babel/helpers@7.23.4: - resolution: {integrity: sha512-HfcMizYz10cr3h29VqyfGL6ZWIjTwWfvYBMsBVGwpcbhNGe3wQ1ZXZRPzZoAHhd9OqHadHqjQ89iVKINXnbzuw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.4 - '@babel/types': 7.23.4 - transitivePeerDependencies: - - supports-color - /@babel/helpers@7.23.5: resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==} engines: {node: '>=6.9.0'} @@ -3931,51 +4068,43 @@ packages: dependencies: '@babel/types': 7.23.5 - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.3): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.3): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5) - /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.3): + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.3): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.5): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.3): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.23.5 /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} @@ -3984,7 +4113,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -3995,14 +4123,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.3): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -4010,57 +4130,48 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.3): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.5): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.3): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.3): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.3): + /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.3): + /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.3): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5): @@ -4070,15 +4181,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} @@ -4087,17 +4189,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.3): - resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} @@ -4109,14 +4200,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.5): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -4124,15 +4207,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.3): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -4141,15 +4215,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.3): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} @@ -4158,15 +4223,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.3): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -4175,15 +4231,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.3): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} @@ -4192,15 +4239,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.3): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} @@ -4209,24 +4247,14 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.3): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.5): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.3): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5): @@ -4237,17 +4265,6 @@ packages: dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.3): - resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} @@ -4259,695 +4276,695 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.3): + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.5): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.5) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5) - /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.3) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.5) - /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.5) - /@babel/plugin-transform-classes@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-classes@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 - /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/template': 7.22.15 - /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.5) - /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.5) - /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.5) - /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.5) - /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 - /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 - /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.3): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.5): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.5) - /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.5) - /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.3 - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5) - /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) - /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5) - /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5) - /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5) - /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-react-constant-elements@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-react-constant-elements@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-display-name@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-react-display-name@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.3): + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.5): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.5) dev: true - /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) '@babel/types': 7.23.4 dev: true - /@babel/plugin-transform-react-pure-annotations@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-react-pure-annotations@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.2 - /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-typescript@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-typescript@7.23.4(@babel/core@7.23.5): resolution: {integrity: sha512-39hCCOl+YUAyMOu6B9SmUTiHUU0t/CxJNUmY3qRdJujbqi+lrQcL11ysYUsAvFWPBdhihrv1z0oRG84Yr3dODQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.5) dev: true - /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5) '@babel/helper-plugin-utils': 7.22.5 - /@babel/preset-env@7.23.3(@babel/core@7.23.3): + /@babel/preset-env@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.3 - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.3) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.3) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.3) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.3) - '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-classes': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.3) - '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.3) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.3) - babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.3) - babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.3) - babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.3) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.5) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.5) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.5) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.5) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-classes': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.5) + '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.5) + babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.5) + babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.5) + babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.5) core-js-compat: 3.33.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.3): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.5): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.23.4 + '@babel/types': 7.23.5 esutils: 2.0.3 - /@babel/preset-react@7.23.3(@babel/core@7.23.3): + /@babel/preset-react@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-transform-react-display-name': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.3) - '@babel/plugin-transform-react-pure-annotations': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-react-display-name': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.5) + '@babel/plugin-transform-react-pure-annotations': 7.23.3(@babel/core@7.23.5) dev: true - /@babel/preset-typescript@7.23.3(@babel/core@7.23.3): + /@babel/preset-typescript@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-typescript': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-typescript': 7.23.4(@babel/core@7.23.5) dev: true /@babel/regjsgen@0.8.0: @@ -4973,23 +4990,6 @@ packages: '@babel/parser': 7.23.4 '@babel/types': 7.23.4 - /@babel/traverse@7.23.4: - resolution: {integrity: sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.4 - '@babel/generator': 7.23.4 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.4 - '@babel/types': 7.23.4 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - /@babel/traverse@7.23.5: resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} engines: {node: '>=6.9.0'} @@ -5447,7 +5447,7 @@ packages: '@chakra-ui/react': 2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) '@emotion/cache': 11.11.0 '@emotion/react': 11.11.1(@types/react@18.2.37)(react@18.2.0) - next: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + next: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) react: 18.2.0 dev: false @@ -5474,7 +5474,7 @@ packages: next: '>=13' react: '>=18' dependencies: - '@chakra-ui/react': 2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/react': 2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) '@emotion/cache': 11.11.0 '@emotion/react': 11.11.1(@types/react@18.2.37)(react@18.2.0) next: 13.5.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0) @@ -5885,6 +5885,77 @@ packages: - '@types/react' dev: false + /@chakra-ui/react@2.8.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==} + peerDependencies: + '@emotion/react': ^11.0.0 + '@emotion/styled': ^11.0.0 + framer-motion: '>=4.0.0' + react: '>=18' + react-dom: '>=18' + dependencies: + '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.16.5)(react@18.2.0) + '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/counter': 2.1.0(react@18.2.0) + '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.1)(react@18.2.0) + '@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.37)(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/hooks': 2.2.1(react@18.2.0) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/live-region': 2.1.0(react@18.2.0) + '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.16.5)(react@18.2.0) + '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.37)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.16.5)(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.2.0) + '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/provider': 2.4.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/react-env': 3.1.0(react@18.2.0) + '@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/styled-system': 2.9.2 + '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@10.16.5)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) + '@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) + '@chakra-ui/theme-utils': 2.0.21 + '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.16.5)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/transition': 2.1.0(framer-motion@10.16.5)(react@18.2.0) + '@chakra-ui/utils': 2.0.15 + '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@emotion/react': 11.11.1(@types/react@18.2.37)(react@18.2.0) + '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) + framer-motion: 10.16.5(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + /@chakra-ui/select@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0): resolution: {integrity: sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==} peerDependencies: @@ -6389,6 +6460,26 @@ packages: react: 18.2.0 dev: false + /@emotion/styled@11.11.0(@emotion/react@11.11.1)(react@18.2.0): + resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.0 + '@emotion/babel-plugin': 11.11.0 + '@emotion/is-prop-valid': 1.2.1 + '@emotion/react': 11.11.1(@types/react@18.2.37)(react@18.2.0) + '@emotion/serialize': 1.1.2 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@emotion/utils': 1.2.1 + react: 18.2.0 + dev: false + /@emotion/unitless@0.7.5: resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} dev: false @@ -8349,7 +8440,7 @@ packages: '@codemirror/view': 6.22.1 dev: false - /@rollup/plugin-babel@5.3.1(@babel/core@7.23.3)(rollup@2.79.1): + /@rollup/plugin-babel@5.3.1(@babel/core@7.23.5)(rollup@2.79.1): resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} engines: {node: '>= 10.0.0'} peerDependencies: @@ -8360,7 +8451,7 @@ packages: '@types/babel__core': optional: true dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 '@babel/helper-module-imports': 7.22.15 '@rollup/pluginutils': 3.1.0(rollup@2.79.1) rollup: 2.79.1 @@ -8972,101 +9063,101 @@ packages: string.prototype.matchall: 4.0.10 dev: false - /@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.23.3): + /@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.3): + /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.5): resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.3): + /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.5): resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.23.3): + /@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.23.3): + /@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.23.3): + /@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.23.3): + /@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.23.3): + /@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.23.5 dev: true - /@svgr/babel-preset@6.5.1(@babel/core@7.23.3): + /@svgr/babel-preset@6.5.1(@babel/core@7.23.5): resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.23.3) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.3) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.3) - '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.23.3) - '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.23.3) - '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.23.3) - '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.23.3) - '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.5) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.5) + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.23.5) dev: true /@svgr/core@6.5.1: resolution: {integrity: sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.23.3 - '@svgr/babel-preset': 6.5.1(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@svgr/babel-preset': 6.5.1(@babel/core@7.23.5) '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) camelcase: 6.3.0 cosmiconfig: 7.1.0 @@ -9088,8 +9179,8 @@ packages: peerDependencies: '@svgr/core': ^6.0.0 dependencies: - '@babel/core': 7.23.3 - '@svgr/babel-preset': 6.5.1(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@svgr/babel-preset': 6.5.1(@babel/core@7.23.5) '@svgr/core': 6.5.1 '@svgr/hast-util-to-babel-ast': 6.5.1 svg-parser: 2.0.4 @@ -9113,11 +9204,11 @@ packages: resolution: {integrity: sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.23.3 - '@babel/plugin-transform-react-constant-elements': 7.23.3(@babel/core@7.23.3) - '@babel/preset-env': 7.23.3(@babel/core@7.23.3) - '@babel/preset-react': 7.23.3(@babel/core@7.23.3) - '@babel/preset-typescript': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/plugin-transform-react-constant-elements': 7.23.3(@babel/core@7.23.5) + '@babel/preset-env': 7.23.3(@babel/core@7.23.5) + '@babel/preset-react': 7.23.3(@babel/core@7.23.5) + '@babel/preset-typescript': 7.23.3(@babel/core@7.23.5) '@svgr/core': 6.5.1 '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) @@ -9363,7 +9454,6 @@ packages: dependencies: '@types/connect': 3.4.38 '@types/node': 20.10.0 - dev: true /@types/btoa-lite@1.0.2: resolution: {integrity: sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==} @@ -9382,7 +9472,6 @@ packages: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: '@types/node': 20.10.0 - dev: true /@types/d3-timer@2.0.3: resolution: {integrity: sha512-jhAJzaanK5LqyLQ50jJNIrB8fjL9gwWZTgYjevPvkDLMU+kTAZkYsobI59nYoeSrH1PucuyJEi247Pb90t6XUg==} @@ -9426,7 +9515,6 @@ packages: '@types/qs': 6.9.10 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - dev: true /@types/express@4.17.21: resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -9435,7 +9523,6 @@ packages: '@types/express-serve-static-core': 4.17.41 '@types/qs': 6.9.10 '@types/serve-static': 1.15.5 - dev: true /@types/fs-extra@8.1.5: resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} @@ -9474,7 +9561,6 @@ packages: /@types/http-errors@2.0.4: resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - dev: true /@types/istanbul-lib-coverage@2.0.6: resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -9559,11 +9645,9 @@ packages: /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - dev: true /@types/mime@3.0.4: resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - dev: true /@types/minimatch@5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} @@ -9583,7 +9667,6 @@ packages: resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} dependencies: '@types/express': 4.17.21 - dev: true /@types/node@10.17.60: resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} @@ -9637,11 +9720,9 @@ packages: /@types/qs@6.9.10: resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} - dev: true /@types/range-parser@1.2.7: resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - dev: true /@types/react-dom@18.0.10: resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==} @@ -9666,7 +9747,7 @@ packages: dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 - csstype: 3.1.2 + csstype: 3.1.3 /@types/request@2.48.12: resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} @@ -9694,7 +9775,6 @@ packages: dependencies: '@types/mime': 1.3.5 '@types/node': 20.10.0 - dev: true /@types/serve-static@1.15.5: resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} @@ -9702,7 +9782,6 @@ packages: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 '@types/node': 20.10.0 - dev: true /@types/stack-utils@2.0.3: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -10678,36 +10757,36 @@ packages: resolve: 1.22.8 dev: false - /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.3): + /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.5): resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/compat-data': 7.23.3 - '@babel/core': 7.23.3 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5) semver: 6.3.1 transitivePeerDependencies: - supports-color - /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.23.3): + /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.23.5): resolution: {integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5) core-js-compat: 3.33.3 transitivePeerDependencies: - supports-color - /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.3): + /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.5): resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5) transitivePeerDependencies: - supports-color @@ -11595,7 +11674,6 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: false /cycle@1.0.3: resolution: {integrity: sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==} @@ -11744,7 +11822,7 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.24.0 dev: false /dayjs@1.11.10: @@ -11822,7 +11900,6 @@ packages: /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: true /decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} @@ -17117,7 +17194,7 @@ packages: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 css-tree: 1.1.3 - csstype: 3.1.2 + csstype: 3.1.3 fastest-stable-stringify: 2.0.2 inline-style-prefixer: 7.0.0 react: 18.2.0 @@ -17187,13 +17264,13 @@ packages: react: '>= 17.0.2' react-i18next: ^12.2.0 dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.24.0 '@types/hoist-non-react-statics': 3.3.5 core-js: 3.33.3 hoist-non-react-statics: 3.3.2 i18next: 22.5.1 i18next-fs-backend: 2.3.0 - next: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + next: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) react: 18.2.0 react-i18next: 12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) dev: false @@ -17207,7 +17284,7 @@ packages: react: '>= 17.0.2' react-i18next: ^12.2.0 dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.24.0 '@types/hoist-non-react-statics': 3.3.5 core-js: 3.33.3 hoist-non-react-statics: 3.3.2 @@ -17227,7 +17304,7 @@ packages: react: '>= 17.0.2' react-i18next: ^12.2.0 dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.24.0 '@types/hoist-non-react-statics': 3.3.5 core-js: 3.33.3 hoist-non-react-statics: 3.3.2 @@ -17247,7 +17324,7 @@ packages: react: '>= 17.0.2' react-i18next: ^12.2.0 dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.24.0 '@types/hoist-non-react-statics': 3.3.5 core-js: 3.33.3 hoist-non-react-statics: 3.3.2 @@ -17267,7 +17344,7 @@ packages: react: '>= 17.0.2' react-i18next: ^12.2.0 dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.24.0 '@types/hoist-non-react-statics': 3.3.5 core-js: 3.33.3 hoist-non-react-statics: 3.3.2 @@ -17300,7 +17377,52 @@ packages: - webpack dev: false - /next@13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): + /next@13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): + resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==} + engines: {node: '>=14.6.0'} + hasBin: true + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^6.0.0 || ^7.0.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + '@next/env': 13.1.6 + '@swc/helpers': 0.4.14 + caniuse-lite: 1.0.30001594 + postcss: 8.4.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + sass: 1.69.5 + styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) + optionalDependencies: + '@next/swc-android-arm-eabi': 13.1.6 + '@next/swc-android-arm64': 13.1.6 + '@next/swc-darwin-arm64': 13.1.6 + '@next/swc-darwin-x64': 13.1.6 + '@next/swc-freebsd-x64': 13.1.6 + '@next/swc-linux-arm-gnueabihf': 13.1.6 + '@next/swc-linux-arm64-gnu': 13.1.6 + '@next/swc-linux-arm64-musl': 13.1.6 + '@next/swc-linux-x64-gnu': 13.1.6 + '@next/swc-linux-x64-musl': 13.1.6 + '@next/swc-win32-arm64-msvc': 13.1.6 + '@next/swc-win32-ia32-msvc': 13.1.6 + '@next/swc-win32-x64-msvc': 13.1.6 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + + /next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==} engines: {node: '>=14.6.0'} hasBin: true @@ -17325,7 +17447,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) sass: 1.69.5 - styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) optionalDependencies: '@next/swc-android-arm-eabi': 13.1.6 '@next/swc-android-arm64': 13.1.6 @@ -17373,7 +17495,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) sass: 1.69.5 - styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) optionalDependencies: '@next/swc-android-arm-eabi': 13.2.4 '@next/swc-android-arm64': 13.2.4 @@ -17464,7 +17586,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) sass: 1.69.5 - styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: @@ -17504,7 +17626,7 @@ packages: postcss: 8.4.31 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) watchpack: 2.4.0 optionalDependencies: '@next/swc-darwin-arm64': 13.5.4 @@ -20408,24 +20530,6 @@ packages: inline-style-parser: 0.1.1 dev: false - /styled-jsx@5.1.1(@babel/core@7.23.3)(react@18.2.0): - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - dependencies: - '@babel/core': 7.23.3 - client-only: 0.0.1 - react: 18.2.0 - dev: false - /styled-jsx@5.1.1(@babel/core@7.23.5)(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -21648,10 +21752,10 @@ packages: engines: {node: '>=10.0.0'} dependencies: '@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0) - '@babel/core': 7.23.3 - '@babel/preset-env': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.23.5 + '@babel/preset-env': 7.23.3(@babel/core@7.23.5) '@babel/runtime': 7.24.0 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.23.3)(rollup@2.79.1) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.23.5)(rollup@2.79.1) '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1) '@rollup/plugin-replace': 2.4.2(rollup@2.79.1) '@surma/rollup-plugin-off-main-thread': 2.2.3 diff --git a/frontend/providers/cloudserver/.env.template b/frontend/providers/cloudserver/.env.template new file mode 100644 index 000000000..8fa703ba7 --- /dev/null +++ b/frontend/providers/cloudserver/.env.template @@ -0,0 +1,3 @@ +NEXT_PUBLIC_MOCK_USER= +LAF_BASE_URL= +JWT_SECRET_APP= \ No newline at end of file diff --git a/frontend/providers/cloudserver/.eslintrc.json b/frontend/providers/cloudserver/.eslintrc.json new file mode 100644 index 000000000..578210f47 --- /dev/null +++ b/frontend/providers/cloudserver/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "next/core-web-vitals", + "rules": { + "react-hooks/rules-of-hooks": 0 + } +} diff --git a/frontend/providers/cloudserver/.gitignore b/frontend/providers/cloudserver/.gitignore new file mode 100644 index 000000000..52364be8e --- /dev/null +++ b/frontend/providers/cloudserver/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts +/public/trainData/ +/.vscode/ +platform.json +.yalc/ +yalc.lock diff --git a/frontend/providers/cloudserver/.prettierignore b/frontend/providers/cloudserver/.prettierignore new file mode 100644 index 000000000..7aef7f6b0 --- /dev/null +++ b/frontend/providers/cloudserver/.prettierignore @@ -0,0 +1,4 @@ +dist +.vscode +**/.DS_Store +node_modules diff --git a/frontend/providers/cloudserver/.prettierrc.js b/frontend/providers/cloudserver/.prettierrc.js new file mode 100644 index 000000000..cb1553cbe --- /dev/null +++ b/frontend/providers/cloudserver/.prettierrc.js @@ -0,0 +1,21 @@ +module.exports = { + printWidth: 100, + tabWidth: 2, + useTabs: false, + semi: true, + singleQuote: true, + quoteProps: 'as-needed', + jsxSingleQuote: false, + trailingComma: 'none', + bracketSpacing: true, + jsxBracketSameLine: false, + arrowParens: 'always', + rangeStart: 0, + rangeEnd: Infinity, + requirePragma: false, + insertPragma: false, + proseWrap: 'preserve', + htmlWhitespaceSensitivity: 'css', + endOfLine: 'lf' +}; + \ No newline at end of file diff --git a/frontend/providers/cloudserver/README.md b/frontend/providers/cloudserver/README.md new file mode 100644 index 000000000..c40d99f59 --- /dev/null +++ b/frontend/providers/cloudserver/README.md @@ -0,0 +1 @@ +# Sealos Cloud Server diff --git a/frontend/providers/cloudserver/deploy/Kubefile b/frontend/providers/cloudserver/deploy/Kubefile new file mode 100644 index 000000000..ec1504e7d --- /dev/null +++ b/frontend/providers/cloudserver/deploy/Kubefile @@ -0,0 +1,13 @@ +FROM scratch + +USER 65532:65532 + +COPY registry registry +COPY manifests manifests + +ENV cloudDomain="127.0.0.1.nip.io" +ENV cloudPort="" +ENV certSecretName="wildcard-cert" +ENV lafBaseUrl="" + +CMD ["kubectl apply -f manifests"] diff --git a/frontend/providers/cloudserver/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/cloudserver/deploy/manifests/appcr.yaml.tmpl new file mode 100644 index 000000000..901196f13 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/manifests/appcr.yaml.tmpl @@ -0,0 +1,19 @@ +apiVersion: app.sealos.io/v1 +kind: App +metadata: + name: cloudserver + namespace: app-system +spec: + data: + desc: cloudserver + url: "https://cloudserver.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" + icon: "https://cloudserver.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg" + menuData: + helpDropDown: false + nameColor: text-black + name: cloudserver + type: iframe + displayType: normal + i18n: + zh: + name: 云主机 diff --git a/frontend/providers/cloudserver/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/cloudserver/deploy/manifests/deploy.yaml.tmpl new file mode 100644 index 000000000..dd8a5f9bc --- /dev/null +++ b/frontend/providers/cloudserver/deploy/manifests/deploy.yaml.tmpl @@ -0,0 +1,83 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app: cloudserver-frontend + name: cloudserver-frontend +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cloudserver-frontend-config + namespace: cloudserver-frontend +data: + config.yaml: |- + addr: :3000 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cloudserver-frontend + namespace: cloudserver-frontend +spec: + replicas: 1 + selector: + matchLabels: + app: cloudserver-frontend + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + app: cloudserver-frontend + spec: + containers: + - name: cloudserver-frontend + env: + - name: LAF_BASE_URL + value: {{ .lafBaseUrl }} + - name: JWT_SECRET_APP + securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - 'ALL' + resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + # do not modify this image, it is used for CI/CD + image: ghcr.io/labring/sealos-cloudserver-frontend:latest + imagePullPolicy: Always + volumeMounts: + - name: cloudserver-frontend-volume + mountPath: /config.yaml + subPath: config.yaml + volumes: + - name: cloudserver-frontend-volume + configMap: + name: cloudserver-frontend-config +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: cloudserver-frontend + name: cloudserver-frontend + namespace: cloudserver-frontend +spec: + ports: + - name: http + port: 3000 + protocol: TCP + targetPort: 3000 + selector: + app: cloudserver-frontend diff --git a/frontend/providers/cloudserver/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/cloudserver/deploy/manifests/ingress.yaml.tmpl new file mode 100644 index 000000000..3136d29ea --- /dev/null +++ b/frontend/providers/cloudserver/deploy/manifests/ingress.yaml.tmpl @@ -0,0 +1,51 @@ +# Copyright © 2023 sealos. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}, https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" + X-Xss-Protection "1; mode=block" + name: cloudserver-frontend + namespace: cloudserver-frontend +spec: + rules: + - host: cloudserver.{{ .cloudDomain }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: cloudserver-frontend + port: + number: 3000 + tls: + - hosts: + - cloudserver.{{ .cloudDomain }} + secretName: {{ .certSecretName }} diff --git a/frontend/providers/cloudserver/i18nrc.json b/frontend/providers/cloudserver/i18nrc.json new file mode 100644 index 000000000..470912080 --- /dev/null +++ b/frontend/providers/cloudserver/i18nrc.json @@ -0,0 +1,9 @@ +{ + "entry": "./public/locales/zh", + "entryLocale": "zh", + "output": "./public/locales", + "outputLocales": [ + "en", + "jp" + ] +} \ No newline at end of file diff --git a/frontend/providers/cloudserver/next-i18next.config.js b/frontend/providers/cloudserver/next-i18next.config.js new file mode 100644 index 000000000..1a032fe9e --- /dev/null +++ b/frontend/providers/cloudserver/next-i18next.config.js @@ -0,0 +1,12 @@ +//next-i18next.config.js +/** + * @type {import('next-i18next').UserConfig} + */ + +module.exports = { + i18n: { + defaultLocale: 'zh', + locales: ['en', 'zh'], + localeDetection: false + } +} diff --git a/frontend/providers/cloudserver/next.config.js b/frontend/providers/cloudserver/next.config.js new file mode 100644 index 000000000..bc4304010 --- /dev/null +++ b/frontend/providers/cloudserver/next.config.js @@ -0,0 +1,30 @@ +/** @type {import('next').NextConfig} */ +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin +const { i18n } = require('./next-i18next.config') +const analyzer = process.env === 'production' ? [new BundleAnalyzerPlugin()] : [] +const path = require('path') + +const nextConfig = { + i18n, + output: 'standalone', + reactStrictMode: false, + compress: true, + webpack: (config, { isServer }) => { + config.module.rules = config.module.rules.concat([ + { + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + use: ['@svgr/webpack'] + } + ]) + config.plugins = [...config.plugins, ...analyzer] + return config + }, + transpilePackages: ['@sealos/ui', 'sealos-desktop-sdk', '@sealos/driver'], + experimental: { + // this includes files from the monorepo base two directories up + outputFileTracingRoot: path.join(__dirname, '../../') + } +} + +module.exports = nextConfig diff --git a/frontend/providers/cloudserver/package.json b/frontend/providers/cloudserver/package.json new file mode 100644 index 000000000..7ccce1513 --- /dev/null +++ b/frontend/providers/cloudserver/package.json @@ -0,0 +1,73 @@ +{ + "name": "cloudserver", + "private": true, + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@chakra-ui/anatomy": "^2.2.1", + "@chakra-ui/icons": "^2.1.1", + "@chakra-ui/next-js": "^2.1.5", + "@chakra-ui/react": "^2.8.1", + "@chakra-ui/system": "^2.6.1", + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@kubernetes/client-node": "^0.18.1", + "@next/font": "13.1.6", + "@sealos/ui": "workspace:^", + "@tanstack/react-query": "^4.35.3", + "@types/multer": "^1.4.10", + "ansi_up": "^5.2.1", + "axios": "^1.5.1", + "date-fns": "^2.30.0", + "dayjs": "^1.11.10", + "decimal.js": "^10.4.3", + "echarts": "^5.4.3", + "framer-motion": "^10.16.4", + "i18next": "^22.5.1", + "immer": "^9.0.21", + "js-cookie": "^3.0.5", + "js-yaml": "^4.1.0", + "jsonwebtoken": "^9.0.2", + "jszip": "^3.10.1", + "lodash": "^4.17.21", + "minio": "^7.1.3", + "mongodb": "^5.9.0", + "multer": "1.4.5-lts.1", + "nanoid": "^4.0.2", + "next": "13.1.6", + "next-i18next": "^13.3.0", + "nprogress": "^0.2.0", + "prettier": "^2.8.8", + "react": "18.2.0", + "react-day-picker": "^8.8.2", + "react-dom": "18.2.0", + "react-hook-form": "^7.46.2", + "react-i18next": "^12.3.1", + "react-markdown": "^8.0.7", + "react-syntax-highlighter": "^15.5.0", + "sass": "^1.68.0", + "sealos-desktop-sdk": "workspace:*", + "typescript": "^5.2.2", + "zustand": "^4.4.1" + }, + "devDependencies": { + "@svgr/webpack": "^6.5.1", + "@types/js-cookie": "^3.0.4", + "@types/js-yaml": "^4.0.6", + "@types/jsonwebtoken": "^9.0.3", + "@types/lodash": "^4.14.199", + "@types/node": "18.13.0", + "@types/nprogress": "^0.2.1", + "@types/react": "18.2.37", + "@types/react-dom": "18.0.10", + "@types/react-syntax-highlighter": "^15.5.7", + "eslint": "8.33.0", + "eslint-config-next": "13.1.6", + "svg-inline-loader": "^0.8.2", + "webpack-bundle-analyzer": "^4.9.1" + } +} \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/favicon.ico b/frontend/providers/cloudserver/public/favicon.ico new file mode 100644 index 000000000..5888f6cfe Binary files /dev/null and b/frontend/providers/cloudserver/public/favicon.ico differ diff --git a/frontend/providers/cloudserver/public/icons/download.svg b/frontend/providers/cloudserver/public/icons/download.svg new file mode 100644 index 000000000..8aed8eea5 --- /dev/null +++ b/frontend/providers/cloudserver/public/icons/download.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/providers/cloudserver/public/icons/sealos-avator.svg b/frontend/providers/cloudserver/public/icons/sealos-avator.svg new file mode 100644 index 000000000..59d5ec5b8 --- /dev/null +++ b/frontend/providers/cloudserver/public/icons/sealos-avator.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/providers/cloudserver/public/icons/sealos.svg b/frontend/providers/cloudserver/public/icons/sealos.svg new file mode 100644 index 000000000..70e732656 --- /dev/null +++ b/frontend/providers/cloudserver/public/icons/sealos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/icons/token.svg b/frontend/providers/cloudserver/public/icons/token.svg new file mode 100644 index 000000000..b0dd76f35 --- /dev/null +++ b/frontend/providers/cloudserver/public/icons/token.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/providers/cloudserver/public/icons/wechat.svg b/frontend/providers/cloudserver/public/icons/wechat.svg new file mode 100644 index 000000000..b713640cb --- /dev/null +++ b/frontend/providers/cloudserver/public/icons/wechat.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/providers/cloudserver/public/locales/en/common.json b/frontend/providers/cloudserver/public/locales/en/common.json new file mode 100644 index 000000000..8d885a008 --- /dev/null +++ b/frontend/providers/cloudserver/public/locales/en/common.json @@ -0,0 +1,91 @@ +{ + "Add Data Disk": "Add Data Disk", + "Amount": "Amount", + "Are you sure to create a cloud host?": "Are you sure you want to create a cloud host?", + "Assign Public IP": "Assign Public IP", + "Auto Generate Password": "Auto Generate Password", + "Automatic Generated": "Automatic Generated", + "BandWidth": "Bandwidth", + "Bandwidth by hour": "Bandwidth by hour", + "Basic Config": "Basic Config", + "Billing items": "Billing items", + "Billing rules": "Billing rules", + "CPU": "CPU", + "Cancel": "Cancel", + "Changing": "Changing", + "Cloud host list": "Cloud Host List", + "Configuration fee details": "Configuration fee details", + "Confirm": "Confirm", + "Copy Failed": "Copy Failed", + "Create Time": "Create Time", + "Created": "Created", + "Creating": "Creating", + "Creation Time": "Creation Time", + "DataDisk": "Data Disk", + "Delete": "Delete", + "Deleted": "Deleted", + "Deleting": "Deleting", + "Deletion warning": "Deletion Warning", + "Describe": "Describe", + "Details": "Details", + "Disk Size": "Disk Size", + "Empty List": "Empty List", + "Fee inquiry in progress": "Fee inquiry in progress...", + "GPU": "GPU", + "IP Address": "IP Address", + "Image": "Image", + "Instance": "Instance", + "Login Method": "Login Method", + "Memory": "Memory", + "Name": "Name", + "New Server": "New Server", + "Operate": "Operate", + "Operating System": "Operating System", + "Operation": "Operation", + "Password copied to clipboard": "Password copied to clipboard", + "Please enter": "Please enter", + "Please enter your password": "Please enter your password", + "Please select a mirror": "Please select a mirror", + "Prompt": "Prompt", + "Public IP": "Public IP", + "Public network bandwidth": "Public network bandwidth", + "Quantity is 1 to 100": "Quantity is 1 to 100", + "ReStart": "Restart", + "Reference fee": "Reference fee", + "Reference fee disk tips": "{{price}}/GiB/hour", + "Reference fee network tips": "When the public network bandwidth is less than 5 Mbps, the price is {{price1}}/Mbps/hour; when the public network bandwidth is greater than or equal to 5 Mbps, the price is {{price2}}/Mbps/hour.", + "Reference fee tip": "{{price}}/hour", + "Restarting": "Restarting", + "Set Password": "Set Password", + "Start": "Start", + "Started": "Started", + "Starting": "Starting", + "Status": "Status", + "Stop": "Stop", + "Stopped": "Stopped", + "Stopping": "Stopping", + "Storage": "Storage", + "Submit": "Submit", + "Submit Error": "Submit Error", + "Supports changing the operating system": "Note: After purchase, you can change the operating system according to your business needs", + "SystemDisk": "System Disk", + "The maximum number of instances is 100": "Maximum number of instances is 100", + "The maximum number of instances is 500": "Maximum number of instances is 500", + "The minimum number of instances is 1": "Minimum number of instances is 1", + "The minimum number of instances is 20": "Minimum number of instances is 20", + "The number of instances cannot be empty": "Number of instances cannot be empty", + "The purpose of the disk": "Disk Purpose", + "Type": "Type", + "capacity": "Capacity", + "close cloud server tip": "Please note that storage will still be charged when the server is shut down. Please confirm!", + "delete cloud server tip": "Are you sure to delete this cloud host?", + "hour": "hour", + "price": "Price", + "publicIpAssigned tips": "Note: Without a dedicated public IP assigned, external communication using a public IP is not possible. The server cannot be used for record filing. Please choose carefully.", + "ssh port": "SSH port", + "sshName": "SSH Username", + "sshPassword": "SSH Password", + "storage fees": "Storage fees", + "success": "Success", + "terminal": "Terminal" +} \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/locales/jp/common.json b/frontend/providers/cloudserver/public/locales/jp/common.json new file mode 100644 index 000000000..fa19905ac --- /dev/null +++ b/frontend/providers/cloudserver/public/locales/jp/common.json @@ -0,0 +1,91 @@ +{ + "Add Data Disk": "データディスクを追加する", + "Amount": "数量", + "Are you sure to create a cloud host?": "クラウドホストを作成してもよろしいですか", + "Assign Public IP": "パブリックIPを割り当てる", + "Auto Generate Password": "パスワードを自動生成する", + "Automatic Generated": "自動生成", + "BandWidth": "帯域幅", + "Bandwidth by hour": "(時間ごとの帯域幅)", + "Basic Config": "基本設定", + "Billing items": "請求項目", + "Billing rules": "請求規則", + "CPU": "CPU", + "Cancel": "キャンセル", + "Changing": "変更中", + "Cloud host list": "クラウドホストリスト", + "Configuration fee details": "設定料金の詳細", + "Confirm": "確認", + "Copy Failed": "コピーに失敗しました", + "Create Time": "作成時間", + "Created": "作成済み", + "Creating": "作成中", + "Creation Time": "作成時間", + "DataDisk": "データディスク", + "Delete": "削除", + "Deleted": "削除済み", + "Deleting": "削除中", + "Deletion warning": "削除警告", + "Describe": "説明", + "Details": "詳細", + "Disk Size": "ディスクサイズ", + "Empty List": "空のリスト", + "Fee inquiry in progress": "料金照会中...", + "GPU": "GPU", + "IP Address": "IPアドレス", + "Image": "イメージ", + "Instance": "インスタンス", + "Login Method": "ログイン方法", + "Memory": "メモリ", + "Name": "名前", + "New Server": "新しいサーバー", + "Operate": "操作", + "Operating System": "オペレーティングシステム", + "Operation": "操作", + "Password copied to clipboard": "パスワードがクリップボードにコピーされました", + "Please enter": "入力してください", + "Please enter your password": "パスワードを入力してください", + "Please select a mirror": "ミラーを選択してください", + "Prompt": "プロンプト", + "Public IP": "パブリックIP", + "Public network bandwidth": "パブリックネットワーク帯域幅", + "Quantity is 1 to 100": "数量は1から100までです", + "ReStart": "再起動", + "Reference fee": "参考料金", + "Reference fee disk tips": "{{price}}/GiB/時間あたり", + "Reference fee network tips": "パブリックネットワークの帯域幅が5 Mbps未満の場合、価格は{{price1}}/Mbps/時間です。帯域幅が5 Mbps以上の場合、価格は{{price2}}/Mbps/時間です。", + "Reference fee tip": "{{price}}/時間", + "Restarting": "再起動中", + "Set Password": "パスワードを設定する", + "Start": "開始", + "Started": "実行中", + "Starting": "起動中", + "Status": "ステータス", + "Stop": "停止", + "Stopped": "停止済み", + "Stopping": "停止中", + "Storage": "ストレージ", + "Submit": "提出する", + "Submit Error": "エラーを送信する", + "Supports changing the operating system": "注意:購入後、オペレーティングシステムの変更がサポートされます。ビジネスのニーズに応じてシステムを変更できます", + "SystemDisk": "システムディスク", + "The maximum number of instances is 100": "最大値は100です", + "The maximum number of instances is 500": "最大値は500です", + "The minimum number of instances is 1": "最小値は1です", + "The minimum number of instances is 20": "最小値は20です", + "The number of instances cannot be empty": "インスタンスの数は空にできません", + "The purpose of the disk": "ディスクの目的", + "Type": "タイプ", + "capacity": "容量", + "close cloud server tip": "クラウドサーバーをシャットダウンしても、ストレージの料金は発生しますので、ご確認ください!", + "delete cloud server tip": "このクラウドサーバーを削除してもよろしいですか", + "hour": "時間", + "price": "価格", + "publicIpAssigned tips": "注意:独立したパブリックIPが割り当てられていないため、外部との通信にはインターネットIPを使用することができません。サーバーは登録に使用できませんので、慎重に選択してください。", + "ssh port": "SSHポート", + "sshName": "SSHユーザー名", + "sshPassword": "SSHパスワード", + "storage fees": "ストレージ料金", + "success": "成功", + "terminal": "ターミナル" +} \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/locales/zh/common.json b/frontend/providers/cloudserver/public/locales/zh/common.json new file mode 100644 index 000000000..62cef639e --- /dev/null +++ b/frontend/providers/cloudserver/public/locales/zh/common.json @@ -0,0 +1,91 @@ +{ + "Cloud host list": "云主机列表", + "New Server": "新建主机", + "Empty List": "空列表", + "Basic Config": "基础配置", + "Submit Error": "提交错误", + "CPU": "CPU", + "Operating System": "操作系统", + "Name": "名称", + "Automatic Generated": "自动生成", + "Submit": "提交", + "Type": "机型", + "Disk Size": "磁盘大小", + "Login Method": "登录方式", + "Set Password": "设置密码", + "Auto Generate Password": "自动生成密码", + "Assign Public IP": "分配公网IP", + "Bandwidth by hour": "(按小时带宽)", + "BandWidth": "带宽", + "Storage": "存储", + "Creation Time": "创建时间", + "Add Data Disk": "新增数据盘", + "Public IP": "公网IP", + "Amount": "数量", + "Describe": "描述", + "Operate": "操作", + "Image": "镜像", + "Please select a mirror": "请选择镜像", + "Supports changing the operating system": "注意:购买后支持更换操作系统,您可根据业务需要变更系统", + "Please enter your password": "请输入登录密码", + "Memory": "内存", + "GPU": "GPU", + "SystemDisk": "系统盘", + "DataDisk": "数据盘", + "The purpose of the disk": "用途", + "capacity": "容量", + "The minimum number of instances is 20": "最小为20", + "The maximum number of instances is 500": "最大数量为500", + "The number of instances cannot be empty": "数量不能为空", + "The minimum number of instances is 1": "最小值为1", + "The maximum number of instances is 100": "最小值为100", + "Details": "详情", + "Start": "启动", + "ReStart": "重启", + "Stop": "关机", + "Delete": "删除", + "Create Time": "创建时间", + "Operation": "操作", + "Status": "状态", + "IP Address": "IP 地址", + "sshName": "ssh 用户名", + "sshPassword": "ssh 密码", + "ssh port": "ssh 端口", + "Creating": "创建中", + "Created": "已创建", + "Starting": "启动中", + "Started": "运行中", + "Stopping": "关机中", + "Stopped": "已关机", + "Deleting": "删除中", + "Deleted": "已删除", + "terminal": "终端", + "success": "成功", + "Deletion warning": "删除警告", + "Cancel": "取消", + "Please enter": "请输入", + "delete cloud server tip": "确认删除这台云主机吗", + "Prompt": "提示", + "Are you sure to create a cloud host?": "确认创建一台云主机吗", + "Confirm": "确认", + "close cloud server tip": "请注意,关机状态下存储仍会收费,请确认!", + "Quantity is 1 to 100": "数量是1到100", + "Password copied to clipboard": "密码已复制到剪切板", + "Copy Failed": "复制失败", + "Changing": "变更中", + "Restarting": "重启中", + "Reference fee": "参考费用", + "Reference fee tip": "{{price}}/小时", + "Configuration fee details": "配置费用明细", + "Instance": "实例", + "storage fees": "存储费用", + "Public network bandwidth": "公网带宽", + "Billing items": "计费项", + "price": "价格", + "publicIpAssigned tips": "注意:未分配独立公网IP,无法使用外网IP对外进行互相通信,服务器无法用于备案,请慎重选择。", + "Fee inquiry in progress": "费用查询中...", + "hour": "小时", + "Reference fee disk tips": "{{price}}/GiB/小时", + "Reference fee network tips": "当公网带宽小于5 Mbps时,价格为{{price1}}/Mbps/小时;当公网带宽大于等于5 Mbps时,价格为{{price2}}/Mbps/小时。", + "Billing rules": "计费规则" +} diff --git a/frontend/providers/cloudserver/public/logo.svg b/frontend/providers/cloudserver/public/logo.svg new file mode 100644 index 000000000..d8db62f9e --- /dev/null +++ b/frontend/providers/cloudserver/public/logo.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/providers/cloudserver/src/api/cloudserver.ts b/frontend/providers/cloudserver/src/api/cloudserver.ts new file mode 100644 index 000000000..822da7cf5 --- /dev/null +++ b/frontend/providers/cloudserver/src/api/cloudserver.ts @@ -0,0 +1,30 @@ +import { UpdateStatusPayload } from '@/pages/api/cloudserver/updateStatus'; +import { POST } from '@/services/request'; +import adaptCloudServerListItem from '@/types/adapt'; +import { CloudServerPrice, CloudServerType, EditForm, OperatingSystems } from '@/types/cloudserver'; +import { CVMInstanceType } from '@/types/cloudserver'; + +export const getCloudServerType = () => POST('/api/cloudserver/listType'); + +export const getCloudServerImage = () => POST('/api/cloudserver/listImage'); + +export const createCloudServer = (payload: EditForm) => POST('/api/cloudserver/create', payload); + +export const delCloudServer = () => POST('/api/cloudserver/delete'); + +export const updateCloudServerStatus = (payload: UpdateStatusPayload) => + POST('/api/cloudserver/updateStatus', payload); + +export const listCloudServer = (payload: { page: number; pageSize: number }) => + POST<{ list: CVMInstanceType[]; page: number; pageSize: number; total: number }>( + '/api/cloudserver/list', + payload + ).then((data) => { + return { + ...data, + list: data?.list?.map(adaptCloudServerListItem) + }; + }); + +export const getCloudServerPrice = (payload: EditForm) => + POST('/api/cloudserver/price', payload); diff --git a/frontend/providers/cloudserver/src/api/platform.ts b/frontend/providers/cloudserver/src/api/platform.ts new file mode 100644 index 000000000..adb989c59 --- /dev/null +++ b/frontend/providers/cloudserver/src/api/platform.ts @@ -0,0 +1,33 @@ +import { SystemEnvResponse } from '@/pages/api/platform/getEnv'; +import { GET, POST } from '@/services/request'; +import { AxiosProgressEvent } from 'axios'; + +export const getSystemEnv = () => GET('/api/platform/getEnv'); + +export const uploadFile = ( + data: FormData, + onUploadProgress?: (progressEvent: AxiosProgressEvent) => void +) => { + return POST< + { + originalName: string; + fileName: string; + }[] + >('/api/minio/upload', data, { + headers: { + 'Content-Type': 'multipart/form-data' + }, + timeout: 3 * 60 * 1000, + onUploadProgress + }); +}; + +export const getFileUrl = ({ fileName }: { fileName: string }) => { + return GET('/api/minio/getUrl', { fileName }); +}; + +export const deleteFileByName = ({ fileName }: { fileName: string }) => { + return GET('/api/minio/delete', { fileName }); +}; + +export const FeishuNotification = () => POST('/api/platform/feishu'); diff --git a/frontend/providers/cloudserver/src/components/AppStatusTag/index.tsx b/frontend/providers/cloudserver/src/components/AppStatusTag/index.tsx new file mode 100644 index 000000000..266944612 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/AppStatusTag/index.tsx @@ -0,0 +1,39 @@ +import { StatusMap } from '@/constants/vm'; +import { PhaseEnum } from '@/types/cloudserver'; +import { Box, Flex, Spinner } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; + +const AppStatusTag = ({ + status, + showBorder = false +}: { + status: PhaseEnum; + showBorder: boolean; +}) => { + const { t } = useTranslation(); + const statusMap = StatusMap[status]; + + return ( + + + + {t(statusMap.label)} + {status?.includes('ing') && } + + + ); +}; + +export default AppStatusTag; diff --git a/frontend/providers/cloudserver/src/components/ButtonGroup/index.tsx b/frontend/providers/cloudserver/src/components/ButtonGroup/index.tsx new file mode 100644 index 000000000..f28defa8b --- /dev/null +++ b/frontend/providers/cloudserver/src/components/ButtonGroup/index.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Flex, Box } from '@chakra-ui/react'; + +const ButtonGroup = ({ + list +}: { + list: { + label: string; + onclick: () => void; + active: boolean; + }[]; +}) => { + return ( + + {list.map((item) => ( + + {item.label} + + ))} + + ); +}; + +export default ButtonGroup; diff --git a/frontend/providers/cloudserver/src/components/CurrencySymbol/index.tsx b/frontend/providers/cloudserver/src/components/CurrencySymbol/index.tsx new file mode 100644 index 000000000..f784def92 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/CurrencySymbol/index.tsx @@ -0,0 +1,22 @@ +import { Text, Icon } from '@chakra-ui/react'; +export default function currencysymbol({ + type = 'shellCoin', + ...props +}: { + type?: 'shellCoin' | 'cny' | 'usd'; +} & Pick[0], 'w' | 'h' | 'color'>) { + return type === 'shellCoin' ? ( + + + + ) : type === 'cny' ? ( + ¥ + ) : ( + $ + ); +} diff --git a/frontend/providers/cloudserver/src/components/FileSelect/index.tsx b/frontend/providers/cloudserver/src/components/FileSelect/index.tsx new file mode 100644 index 000000000..3f1a7fec5 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/FileSelect/index.tsx @@ -0,0 +1,255 @@ +import { useLoading } from '@/hooks/useLoading'; +import { useSelectFile } from '@/hooks/useSelectFile'; +import { useToast } from '@/hooks/useToast'; +import { Box, Center, Flex, Icon, Text, type BoxProps } from '@chakra-ui/react'; +import { customAlphabet } from 'nanoid'; +import { useTranslation } from 'next-i18next'; +import { DragEvent, useCallback, useState } from 'react'; + +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); + +export const FileImgs = [ + { + reg: /\.yaml$/i, + src: ( + + + + ) + }, + { + reg: /\.(svg|png|jpg|jpeg|gif|bmp)$/i, + src: ( + + + + + + ) + } +]; + +export type FileItemType = { + id: string; + filename: string; + text?: string; + file: File; + icon: JSX.Element; +}; +interface Props extends BoxProps { + fileExtension: string; + files: FileItemType[]; + setFiles: React.Dispatch>; +} + +const FileSelect = ({ fileExtension, setFiles, files, ...BoxStyle }: Props) => { + const { Loading: FileSelectLoading } = useLoading(); + const { t } = useTranslation(); + const { toast } = useToast(); + const [isDragging, setIsDragging] = useState(false); + const [selecting, setSelecting] = useState(false); + + const { File, onOpen } = useSelectFile({ + fileType: fileExtension, + multiple: true + }); + + const onSelectFile = useCallback( + async (files: File[]) => { + setSelecting(true); + try { + // Parse file by file + let promise = Promise.resolve([]); + files.forEach((file) => { + promise = promise.then(async (result) => { + let icon = FileImgs.find((item) => item.reg.test(file.name))?.src; + if (!icon) { + icon = FileImgs[0].src; + } + const fileItem: FileItemType = { + id: nanoid(), + filename: file.name, + icon, + file: file + }; + return [...result, fileItem]; + }); + }); + const chunkFiles = await promise; + console.log(chunkFiles); + + setFiles(chunkFiles); + } catch (error: any) { + console.log(error); + toast({ + title: typeof error === 'string' ? error : '解析文件失败', + status: 'error' + }); + } + setSelecting(false); + }, + [setFiles, toast] + ); + + const handleDragEnter = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }; + + const handleDrop = useCallback( + async (e: DragEvent) => { + e.preventDefault(); + setIsDragging(false); + const items = e.dataTransfer.items; + const fileList: File[] = []; + + if (e.dataTransfer.items.length <= 1) { + const traverseFileTree = async (item: any) => { + return new Promise((resolve, reject) => { + if (item.isFile) { + item.file((file: File) => { + fileList.push(file); + resolve(); + }); + } else if (item.isDirectory) { + const dirReader = item.createReader(); + dirReader.readEntries(async (entries: any[]) => { + for (let i = 0; i < entries.length; i++) { + await traverseFileTree(entries[i]); + } + resolve(); + }); + } + }); + }; + for (let i = 0; i < items.length; i++) { + const item = items[i].webkitGetAsEntry(); + if (item) { + await traverseFileTree(item); + } + } + } else { + const files = Array.from(e.dataTransfer.files); + let isErr = files.some((item) => item.type === ''); + if (isErr) { + return toast({ + title: t('file.upload error description'), + status: 'error' + }); + } + for (let i = 0; i < files.length; i++) { + fileList.push(files[i]); + } + } + onSelectFile(fileList); + }, + [onSelectFile, t, toast] + ); + + return ( + + + + + + + {t('Upload File')} + + {/* + {files.map((item) => ( + + {item.icon} + {item.filename} + { + setFiles((state) => state.filter((file) => file.id !== item.id)); + }} + > + + + + ))} + */} + + + + + + {files.map((item) => ( + +
+ {item.icon} +
+ {item.filename} + { + setFiles((state) => state.filter((file) => file.id !== item.id)); + }} + > + + +
+ ))} +
+
+ ); +}; + +export default FileSelect; diff --git a/frontend/providers/cloudserver/src/components/FormControl/index.tsx b/frontend/providers/cloudserver/src/components/FormControl/index.tsx new file mode 100644 index 000000000..904350290 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/FormControl/index.tsx @@ -0,0 +1,29 @@ +import React, { ReactNode } from 'react'; +import { FormControl, FormErrorMessage } from '@chakra-ui/react'; +import type { FormControlProps } from '@chakra-ui/react'; + +interface Props extends FormControlProps { + errorText?: string; + showError?: boolean; + children: ReactNode; +} + +const MyFormControl = ({ errorText, showError = false, children, ...props }: Props) => { + return ( + + {children} + {showError && !!errorText && ( + + {errorText} + + )} + + ); +}; + +export default MyFormControl; diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/analyze.svg b/frontend/providers/cloudserver/src/components/Icon/icons/analyze.svg new file mode 100644 index 000000000..503178647 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/analyze.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/arrowLeft.svg b/frontend/providers/cloudserver/src/components/Icon/icons/arrowLeft.svg new file mode 100644 index 000000000..7905b5380 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/arrowLeft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/change.svg b/frontend/providers/cloudserver/src/components/Icon/icons/change.svg new file mode 100644 index 000000000..d81483220 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/change.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/close.svg b/frontend/providers/cloudserver/src/components/Icon/icons/close.svg new file mode 100644 index 000000000..62fb9a2a8 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/close.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/connection.svg b/frontend/providers/cloudserver/src/components/Icon/icons/connection.svg new file mode 100644 index 000000000..393e9e2eb --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/connection.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/continue.svg b/frontend/providers/cloudserver/src/components/Icon/icons/continue.svg new file mode 100644 index 000000000..998357d4a --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/continue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/copy.svg b/frontend/providers/cloudserver/src/components/Icon/icons/copy.svg new file mode 100644 index 000000000..bfd38df8e --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/delete.svg b/frontend/providers/cloudserver/src/components/Icon/icons/delete.svg new file mode 100644 index 000000000..7f07bdcd4 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/delete.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/detail.svg b/frontend/providers/cloudserver/src/components/Icon/icons/detail.svg new file mode 100644 index 000000000..380ce9fff --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/detail.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/download.svg b/frontend/providers/cloudserver/src/components/Icon/icons/download.svg new file mode 100644 index 000000000..55919030d --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/formInfo.svg b/frontend/providers/cloudserver/src/components/Icon/icons/formInfo.svg new file mode 100644 index 000000000..164e7a19d --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/formInfo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/help.svg b/frontend/providers/cloudserver/src/components/Icon/icons/help.svg new file mode 100644 index 000000000..0365857b3 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/help.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/info.svg b/frontend/providers/cloudserver/src/components/Icon/icons/info.svg new file mode 100644 index 000000000..5f81980ab --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/link.svg b/frontend/providers/cloudserver/src/components/Icon/icons/link.svg new file mode 100644 index 000000000..126255b73 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/link.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/log.svg b/frontend/providers/cloudserver/src/components/Icon/icons/log.svg new file mode 100644 index 000000000..7dedd37e5 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/logo.svg b/frontend/providers/cloudserver/src/components/Icon/icons/logo.svg new file mode 100644 index 000000000..d826088c3 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/more.svg b/frontend/providers/cloudserver/src/components/Icon/icons/more.svg new file mode 100644 index 000000000..bc3ddc9e9 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/more.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/noEvents.svg b/frontend/providers/cloudserver/src/components/Icon/icons/noEvents.svg new file mode 100644 index 000000000..9665a7024 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/noEvents.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/pause.svg b/frontend/providers/cloudserver/src/components/Icon/icons/pause.svg new file mode 100644 index 000000000..0c7f9ec92 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/pause.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg b/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg new file mode 100644 index 000000000..828a964b1 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/podList.svg b/frontend/providers/cloudserver/src/components/Icon/icons/podList.svg new file mode 100644 index 000000000..6be85b62a --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/podList.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/read.svg b/frontend/providers/cloudserver/src/components/Icon/icons/read.svg new file mode 100644 index 000000000..3140dd585 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/read.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/restart.svg b/frontend/providers/cloudserver/src/components/Icon/icons/restart.svg new file mode 100644 index 000000000..93cf5ef2f --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/restart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/restore.svg b/frontend/providers/cloudserver/src/components/Icon/icons/restore.svg new file mode 100644 index 000000000..2e89f43b6 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/restore.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/settings.svg b/frontend/providers/cloudserver/src/components/Icon/icons/settings.svg new file mode 100644 index 000000000..3cd5c9e17 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/start.svg b/frontend/providers/cloudserver/src/components/Icon/icons/start.svg new file mode 100644 index 000000000..c0603e79a --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/start.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/statusDetail.svg b/frontend/providers/cloudserver/src/components/Icon/icons/statusDetail.svg new file mode 100644 index 000000000..9716b6471 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/statusDetail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/terminal.svg b/frontend/providers/cloudserver/src/components/Icon/icons/terminal.svg new file mode 100644 index 000000000..db9147be9 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/terminal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/unread.svg b/frontend/providers/cloudserver/src/components/Icon/icons/unread.svg new file mode 100644 index 000000000..29403d117 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/unread.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/warning.svg b/frontend/providers/cloudserver/src/components/Icon/icons/warning.svg new file mode 100644 index 000000000..104b2dba1 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/index.tsx b/frontend/providers/cloudserver/src/components/Icon/index.tsx new file mode 100644 index 000000000..ad6b3ca1f --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/index.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import type { IconProps } from '@chakra-ui/react'; +import { Icon } from '@chakra-ui/react'; + +const map = { + more: require('./icons/more.svg').default, + podList: require('./icons/podList.svg').default, + arrowLeft: require('./icons/arrowLeft.svg').default, + plus: require('./icons/plus.svg').default, + delete: require('./icons/delete.svg').default, + restart: require('./icons/restart.svg').default, + start: require('./icons/start.svg').default, + pause: require('./icons/pause.svg').default, + detail: require('./icons/detail.svg').default, + logo: require('./icons/logo.svg').default, + change: require('./icons/change.svg').default, + formInfo: require('./icons/formInfo.svg').default, + settings: require('./icons/settings.svg').default, + copy: require('./icons/copy.svg').default, + continue: require('./icons/continue.svg').default, + noEvents: require('./icons/noEvents.svg').default, + warning: require('./icons/warning.svg').default, + analyze: require('./icons/analyze.svg').default, + terminal: require('./icons/terminal.svg').default, + log: require('./icons/log.svg').default, + statusDetail: require('./icons/statusDetail.svg').default, + read: require('./icons/read.svg').default, + unread: require('./icons/unread.svg').default, + connection: require('./icons/connection.svg').default, + info: require('./icons/info.svg').default, + restore: require('./icons/restore.svg').default, + download: require('./icons/download.svg').default, + close: require('./icons/close.svg').default, + help: require('./icons/help.svg').default, + link: require('./icons/link.svg').default +}; + +const MyIcon = ({ + name, + w = 'auto', + h = 'auto', + ...props +}: { name: keyof typeof map } & IconProps) => { + return map[name] ? ( + + ) : null; +}; + +export default MyIcon; diff --git a/frontend/providers/cloudserver/src/components/MyTable/index.tsx b/frontend/providers/cloudserver/src/components/MyTable/index.tsx new file mode 100644 index 000000000..e78b45355 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/MyTable/index.tsx @@ -0,0 +1,93 @@ +import React, { useState } from 'react'; +import { Box, BoxProps, Grid, Flex } from '@chakra-ui/react'; + +export type TableColumnsType = { + title: string; + dataIndex?: string; + key: string; + render?: (item: any, rowNumber: number, activeId: number) => JSX.Element; +}; + +interface Props extends BoxProps { + columns: TableColumnsType[]; + data: any[]; + itemClass?: string; + openSelected?: boolean; + onRowClick?: (item: any) => void; +} + +export const MyTable = ({ + columns, + data, + itemClass = '', + openSelected = false, + onRowClick, + ...reset +}: Props) => { + const [activeId, setActiveId] = useState(0); + + return ( + + + {columns.map((item, i) => ( + + {item.title} + + ))} + + {data.map((item: any, index1) => ( + { + setActiveId(index1); + onRowClick && onRowClick(item); + }} + > + {columns.map((col, index2) => ( + + {col.render + ? col.render(item, index1, activeId) + : col.dataIndex + ? `${item[col.dataIndex]}` + : ''} + + ))} + + ))} + + ); +}; diff --git a/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx b/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx new file mode 100644 index 000000000..d51261c56 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Tooltip, TooltipProps } from '@chakra-ui/react'; + +const MyTooltip = ({ children, ...props }: TooltipProps) => { + return ( + + {children} + + ); +}; + +export default MyTooltip; diff --git a/frontend/providers/cloudserver/src/components/Pagination/index.tsx b/frontend/providers/cloudserver/src/components/Pagination/index.tsx new file mode 100644 index 000000000..57650ae25 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Pagination/index.tsx @@ -0,0 +1,132 @@ +import { Flex, FlexProps, Icon, Text } from '@chakra-ui/react'; +import { Dispatch, SetStateAction, useState } from 'react'; + +type PaginationProps = { + totalItems: number; + itemsPerPage: number; + currentPage: number; + setCurrentPage: Dispatch>; +}; + +export default function Pagination({ + totalItems, + itemsPerPage, + currentPage = 1, + setCurrentPage +}: PaginationProps) { + const totalPage = Math.ceil(totalItems / itemsPerPage); + + const goToPage = (page: number) => { + if (page >= 1 && page <= totalPage) { + setCurrentPage(page); + } + }; + + const handlePrevPage = () => { + goToPage(currentPage - 1); + }; + + const handleNextPage = () => { + goToPage(currentPage + 1); + }; + + const buttonStyle: FlexProps = { + justifyContent: 'center', + alignItems: 'center', + w: '24px', + h: '24px', + borderRadius: '50%' + }; + + return ( + + Total: + {totalItems} + + goToPage(1)} + {...buttonStyle} + {...{ + bg: currentPage === 1 ? '#EDF0F2' : '#EDEFF1', + cursor: currentPage === 1 ? 'not-allowed' : 'pointer', + opacity: currentPage === 1 ? '0.5' : '1' + }} + > + + + + + + + + + + + + {currentPage}/{totalPage} + + + + + + + + goToPage(totalPage)} + > + + + + + + {itemsPerPage} + /Page + + ); +} diff --git a/frontend/providers/cloudserver/src/components/RangeDate/index.tsx b/frontend/providers/cloudserver/src/components/RangeDate/index.tsx new file mode 100644 index 000000000..c46b79ae2 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/RangeDate/index.tsx @@ -0,0 +1,70 @@ +import { + Flex, + Input, + Popover, + PopoverTrigger, + Img, + PopoverContent, + Button, + Box, + useTheme +} from '@chakra-ui/react'; +import { format, parse, isValid, isAfter, isBefore } from 'date-fns'; +import { useState, ChangeEventHandler, useEffect, useMemo } from 'react'; +import { DateRange, SelectRangeEventHandler, DayPicker } from 'react-day-picker'; +import { useQueryClient } from '@tanstack/react-query'; +import { CalendarIcon } from '@chakra-ui/icons'; + +export default function RangeDate({ + isDisabled = false, + defaultRange = { from: new Date(), to: new Date() } +}: { + isDisabled?: boolean; + defaultRange?: DateRange; +}) { + const [selectedRange, setSelectedRange] = useState(defaultRange); + + const onClose = () => {}; + + return ( + + + + + + { + if (e) { + setSelectedRange(e); + } else { + setSelectedRange((state) => ({ + from: state.from, + to: state.from + })); + } + }} + /> + + + ); +} diff --git a/frontend/providers/cloudserver/src/components/RangeDate/style.module.scss b/frontend/providers/cloudserver/src/components/RangeDate/style.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/providers/cloudserver/src/components/SelectDateRange/index.tsx b/frontend/providers/cloudserver/src/components/SelectDateRange/index.tsx new file mode 100644 index 000000000..635f189f4 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/SelectDateRange/index.tsx @@ -0,0 +1,289 @@ +import { + Box, + Button, + Flex, + Icon, + Input, + Popover, + PopoverContent, + PopoverTrigger +} from '@chakra-ui/react'; +import { endOfDay, format, isAfter, isBefore, isValid, parse, startOfDay } from 'date-fns'; +import { ChangeEventHandler, Dispatch, SetStateAction, useState } from 'react'; +import { DateRange, DayPicker, SelectRangeEventHandler } from 'react-day-picker'; + +type SelectDateRangeProps = { + isDisabled?: boolean; + startTime: Date; + setStartTime: Dispatch>; + endTime: Date; + setEndTime: Dispatch>; +}; + +export default function SelectDateRange({ + isDisabled, + startTime, + setStartTime, + endTime, + setEndTime +}: SelectDateRangeProps) { + const initState = { from: startTime, to: endTime }; + + const [selectedRange, setSelectedRange] = useState(initState); + const [fromValue, setFromValue] = useState(format(initState.from, 'y-MM-dd')); + const [toValue, setToValue] = useState(format(initState.to, 'y-MM-dd')); + const [inputState, setInputState] = useState<0 | 1>(0); + const onClose = () => { + selectedRange?.from && setStartTime(startOfDay(selectedRange.from)); + selectedRange?.to && setEndTime(endOfDay(selectedRange.to)); + }; + + const handleFromChange: ChangeEventHandler = (e) => { + setFromValue(e.target.value); + const date = parse(e.target.value, 'y-MM-dd', new Date()); + if (!isValid(date)) { + return setSelectedRange({ from: undefined, to: selectedRange?.to }); + } + + if (selectedRange?.to) { + if (isAfter(date, selectedRange.to)) { + setSelectedRange({ from: selectedRange.to, to: date }); + } else { + setSelectedRange({ from: date, to: selectedRange?.to }); + } + } else { + setSelectedRange({ from: date, to: date }); + } + }; + + const handleToChange: ChangeEventHandler = (e) => { + setToValue(e.target.value); + const date = parse(e.target.value, 'y-MM-dd', new Date()); + + if (!isValid(date)) { + return setSelectedRange({ from: selectedRange?.from, to: undefined }); + } + if (selectedRange?.from) { + if (isBefore(date, selectedRange.from)) { + setSelectedRange({ from: date, to: selectedRange.from }); + } else { + setSelectedRange({ from: selectedRange?.from, to: date }); + } + } else { + setSelectedRange({ from: date, to: date }); + } + }; + + const handleRangeSelect: SelectRangeEventHandler = (range: DateRange | undefined) => { + if (range) { + let { from, to } = range; + if (inputState === 0) { + if (from === selectedRange?.from) { + from = to; + } else { + to = from; + } + setInputState(1); + } else { + setInputState(0); + } + setSelectedRange({ + from, + to + }); + if (from) { + setFromValue(format(from, 'y-MM-dd')); + } else { + setFromValue(''); + } + if (to) { + setToValue(format(to, 'y-MM-dd')); + } else { + setToValue(from ? format(from, 'y-MM-dd') : ''); + } + } else { + if (fromValue && selectedRange?.from) { + setToValue(fromValue); + setSelectedRange({ + ...selectedRange, + to: selectedRange.from + }); + setInputState(1); + } + } + }; + + const handleRangeSelectFrom: SelectRangeEventHandler = (range: DateRange | undefined) => { + if (range) { + let { from, to } = range; + if (selectedRange?.to) { + if (from) { + if (!to) { + to = from; + } else if (from === selectedRange?.from) { + from = to; + to = selectedRange.to; + } + if (isBefore(from, selectedRange.to)) { + setSelectedRange({ + ...selectedRange, + from + }); + setFromValue(format(from, 'y-MM-dd')); + } + } + } + } + }; + + const handleRangeSelectTo: SelectRangeEventHandler = (range: DateRange | undefined) => { + console.log(range, selectedRange); + if (range) { + let { from, to } = range; + if (selectedRange?.from) { + if (to) { + if (!from) { + from = to; + } else if (to === selectedRange?.to) { + to = from; + from = selectedRange.from; + } + if (isAfter(to, selectedRange.from)) { + setSelectedRange({ + ...selectedRange, + to + }); + setToValue(format(to, 'y-MM-dd')); + } + } + } + } else { + if (fromValue && selectedRange?.from) { + setToValue(fromValue); + setSelectedRange({ + ...selectedRange, + to: selectedRange.from + }); + setInputState(1); + } + } + }; + + return ( + + + + + + + + + + - + + + + + + + + + + { + setInputState(0); + onClose(); + }} + > + + + + + + + + + ); +} diff --git a/frontend/providers/cloudserver/src/components/Tip/index.tsx b/frontend/providers/cloudserver/src/components/Tip/index.tsx new file mode 100644 index 000000000..de7031bb4 --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Tip/index.tsx @@ -0,0 +1,66 @@ +import React, { useMemo } from 'react'; +import { BoxProps, Flex, Box } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; + +interface Props extends BoxProps { + text: string; + icon?: JSX.Element; + theme?: 'blue'; + size?: 'sm' | 'md' | 'lg'; +} + +const Tip = ({ size = 'md', text, icon, theme, ...props }: Props) => { + const { t } = useTranslation(); + + const sizeMap = useMemo(() => { + switch (size) { + case 'sm': + return { + fontSize: '12px', + px: 3, + py: 1 + }; + case 'md': + return { + fontSize: '14px', + px: 4, + py: 2 + }; + case 'lg': + return { + fontSize: '16px', + px: 5, + py: 2 + }; + default: + return {}; + } + }, [size]); + const themeMap = useMemo(() => { + switch (theme) { + case 'blue': + return { + color: '#0884DD', + bg: '#ECF8FF' + }; + default: + return { + color: '#0884DD', + bg: '#ECF8FF' + }; + } + }, [theme]); + + return ( + + {icon ? ( + + {icon} + + ) : null} + {t(text)} + + ); +}; + +export default Tip; diff --git a/frontend/providers/cloudserver/src/constants/theme.ts b/frontend/providers/cloudserver/src/constants/theme.ts new file mode 100644 index 000000000..3b6677ecc --- /dev/null +++ b/frontend/providers/cloudserver/src/constants/theme.ts @@ -0,0 +1,16 @@ +import { extendTheme } from '@chakra-ui/react'; +import { theme as sealosTheme } from '@sealos/ui'; + +export const theme = extendTheme(sealosTheme, { + styles: { + global: { + 'html, body': { + fontSize: 'md', + height: '100%', + overflow: 'overlay', + fontWeight: 400, + minWidth: '1024px' + } + } + } +}); diff --git a/frontend/providers/cloudserver/src/constants/vm.ts b/frontend/providers/cloudserver/src/constants/vm.ts new file mode 100644 index 000000000..49e7e92de --- /dev/null +++ b/frontend/providers/cloudserver/src/constants/vm.ts @@ -0,0 +1,405 @@ +import { PhaseEnum } from '@/types/cloudserver'; + +export const SliderList = { + cpu: [ + { + label: '0.1', + value: 100 + }, + { + label: '0.2', + value: 200 + }, + { + label: '0.5', + value: 500 + }, + { + label: '1', + value: 1000 + }, + { + label: '2', + value: 2000 + }, + { + label: '3', + value: 3000 + }, + { + label: '4', + value: 4000 + }, + { + label: '8', + value: 8000 + } + ], + memory: [ + { + label: '256 M', + value: 256 + }, + { + label: '512 M', + value: 512 + }, + { + label: '1 G', + value: 1024 + }, + { + label: '2 G', + value: 2048 + }, + { + label: '4 G', + value: 4096 + }, + { + label: '8 G', + value: 8192 + }, + { + label: '16 G', + value: 16384 + }, + { + label: '32 G', + value: 32768 + } + ] +}; + +export const MockInstanceType = [ + { + CPU: 32, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.8XLARGE64', + Memory: 64, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 16, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.4XLARGE32', + Memory: 32, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 8, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.2XLARGE32', + Memory: 32, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 8, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.2XLARGE16', + Memory: 16, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 4, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.LARGE8', + Memory: 8, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 4, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.LARGE16', + Memory: 16, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 32, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.8XLARGE128', + Memory: 128, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 16, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.4XLARGE64', + Memory: 64, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 2, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.MEDIUM4', + Memory: 4, + Zone: 'ap-guangzhou-6' + }, + { + CPU: 2, + FPGA: 0, + GPU: 0, + GpuCount: 0, + InstanceFamily: 'TS5', + InstanceType: 'TS5.MEDIUM8' + } +]; + +export const MockStorages = [ + { + id: '1', + type: 'Hard Drive', + size: '1TB', + amount: '2', + describe: 'Internal storage for desktop computer' + }, + { + id: '2', + type: 'SSD', + size: '500GB', + amount: '1', + describe: 'Solid-state drive for laptop' + }, + { + id: '1', + type: 'External HDD', + size: '2TB', + amount: '3', + describe: 'Portable storage for backup' + }, + { + type: 'USB Flash Drive', + size: '64GB', + amount: '5', + describe: 'Removable storage for transferring files' + } +]; + +export const MockImage = [ + { + name: 'OpenCloudOS', + type: 'Linux', + description: 'An open-source cloud operating system designed for scalability and flexibility.', + logo: 'https://example.com/opencloudos.png' + }, + { + name: 'TencentOS', + type: 'Linux', + description: + 'A Linux distribution developed by Tencent, optimized for cloud computing and internet services.', + logo: 'https://example.com/tencentos.png' + }, + { + name: 'CentOS', + type: 'Linux', + description: + 'A free and open-source Linux distribution based on Red Hat Enterprise Linux (RHEL).', + logo: 'https://example.com/centos.png' + }, + { + name: 'Windows', + type: 'Windows', + description: + 'A popular operating system developed by Microsoft, widely used for personal computers.', + logo: 'https://example.com/windows.png' + }, + { + name: 'Ubuntu', + type: 'Linux', + description: + 'A user-friendly Linux distribution based on Debian, known for its ease of use and community support.', + logo: 'https://example.com/ubuntu.png' + }, + { + name: 'Debian', + type: 'Linux', + description: + 'A stable and versatile Linux distribution, widely used as a base for other distributions.', + logo: 'https://example.com/debian.png' + }, + { + name: 'CentOS Stream', + type: 'Linux', + description: 'A rolling-release Linux distribution that serves as a testing ground for RHEL.', + logo: 'https://example.com/centos-stream.png' + }, + { + name: 'AlmaLinux', + type: 'Linux', + description: + 'A community-driven Linux distribution designed as a drop-in replacement for CentOS.', + logo: 'https://example.com/almalinux.png' + }, + { + name: 'CoreOS', + type: 'Linux', + description: + 'A lightweight Linux distribution designed for containerized applications and cloud deployments.', + logo: 'https://example.com/coreos.png' + }, + { + name: 'openSUSE', + type: 'Linux', + description: + 'A general-purpose Linux distribution known for its stability and comprehensive package management.', + logo: 'https://example.com/opensuse.png' + }, + { + name: 'Rocky Linux', + type: 'Linux', + description: + 'A community-driven enterprise-grade Linux distribution, created as a replacement for CentOS.', + logo: 'https://example.com/rockylinux.png' + }, + { + name: 'FreeBSD', + type: 'BSD', + description: + 'A free and open-source Unix-like operating system based on the Berkeley Software Distribution (BSD).', + logo: 'https://example.com/freebsd.png' + }, + { + name: 'Fedora', + type: 'Linux', + description: + 'A cutting-edge Linux distribution sponsored by Red Hat, known for its frequent updates and new features.', + logo: 'https://example.com/fedora.png' + } +]; + +export const MockInstance = [ + { + _id: '60a7e9e8c8e8a5001f9e8a5a', + phase: 'Running', + state: 'Active', + namespace: 'default', + sealosUserId: '123456', + cpu: 4, + disk: 100, + memory: 8192, + publicNetworkAccess: true, + instanceId: 'i-1234567890', + ImageId: 'ami-12345678', + InstanceName: 'MyVM1', + loginName: 'admin', + loginPassword: 'password123', + cloudProvider: 'AWS' + }, + { + _id: '60a7e9e8c8e8a5001f9e8a5b', + phase: 'Stopped', + state: 'Inactive', + namespace: 'default', + sealosUserId: '789012', + cpu: 2, + disk: 50, + memory: 4096, + publicNetworkAccess: false, + instanceId: 'i-0987654321', + ImageId: 'ami-87654321', + InstanceName: 'MyVM2', + loginName: 'admin', + loginPassword: 'password456', + cloudProvider: 'Azure' + } +]; + +export const StatusMap = { + [PhaseEnum.Starting]: { + label: 'Starting', + value: PhaseEnum.Starting, + color: 'green.600', + backgroundColor: 'green.50', + dotColor: 'green.600' + }, + [PhaseEnum.Started]: { + label: 'Started', + value: PhaseEnum.Started, + color: 'green.600', + backgroundColor: 'green.50', + dotColor: 'green.600' + }, + [PhaseEnum.Creating]: { + label: 'Creating', + value: PhaseEnum.Creating, + color: 'grayModern.500', + backgroundColor: 'grayModern.100', + dotColor: 'grayModern.500' + }, + [PhaseEnum.Created]: { + label: 'Created', + value: PhaseEnum.Created, + color: 'grayModern.500', + backgroundColor: 'grayModern.100', + dotColor: 'grayModern.500' + }, + [PhaseEnum.Deleted]: { + label: 'Deleted', + value: PhaseEnum.Deleted, + color: 'rgba(240, 68, 56, 1)', + backgroundColor: 'rgba(254, 243, 242, 1)', + dotColor: 'rgba(240, 68, 56, 1)' + }, + [PhaseEnum.Deleting]: { + label: 'Deleting', + value: PhaseEnum.Deleting, + color: 'rgba(240, 68, 56, 1)', + backgroundColor: 'rgba(254, 243, 242, 1)', + dotColor: 'rgba(240, 68, 56, 1)' + }, + [PhaseEnum.Stopped]: { + label: 'Stopped', + value: PhaseEnum.Stopped, + color: 'rgba(111, 93, 215, 1)', + backgroundColor: 'rgba(240, 238, 255, 1)', + dotColor: 'rgba(111, 93, 215, 1)' + }, + [PhaseEnum.Stopping]: { + label: 'Stopping', + value: PhaseEnum.Stopping, + color: 'rgba(111, 93, 215, 1)', + backgroundColor: 'rgba(240, 238, 255, 1)', + dotColor: 'rgba(111, 93, 215, 1)' + }, + [PhaseEnum.Changing]: { + label: 'Changing', + value: PhaseEnum.Changing, + color: 'grayModern.500', + backgroundColor: 'grayModern.100', + dotColor: 'grayModern.500' + }, + [PhaseEnum.Restarting]: { + label: 'Restarting', + value: PhaseEnum.Restarting, + color: 'grayModern.500', + backgroundColor: 'grayModern.100', + dotColor: 'grayModern.500' + } +}; diff --git a/frontend/providers/cloudserver/src/hooks/useConfirm.tsx b/frontend/providers/cloudserver/src/hooks/useConfirm.tsx new file mode 100644 index 000000000..92103a860 --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useConfirm.tsx @@ -0,0 +1,80 @@ +import { useCallback, useRef } from 'react'; +import { + AlertDialog, + AlertDialogBody, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogContent, + AlertDialogOverlay, + useDisclosure, + Button +} from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; + +export const useConfirm = ({ + title = 'Prompt', + content, + confirmText = 'Confirm' +}: { + title?: string; + content: string; + confirmText?: string; +}) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + const { t } = useTranslation(); + const cancelRef = useRef(null); + const confirmCb = useRef(); + const cancelCb = useRef(); + + return { + openConfirm: useCallback( + (confirm?: any, cancel?: any) => { + return function () { + onOpen(); + confirmCb.current = confirm; + cancelCb.current = cancel; + }; + }, + [onOpen] + ), + ConfirmChild: useCallback( + () => ( + + + + + {t(title)} + + + {content} + + + + + + + + + ), + [confirmText, content, isOpen, onClose, t, title] + ) + }; +}; diff --git a/frontend/providers/cloudserver/src/hooks/useCopyData.ts b/frontend/providers/cloudserver/src/hooks/useCopyData.ts new file mode 100644 index 000000000..0287e014a --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useCopyData.ts @@ -0,0 +1,36 @@ +import { UseToastOptions } from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useTranslation } from 'next-i18next'; + +/** + * copy text data + */ +export const useCopyData = () => { + const { message: toast } = useMessage(); + const { t } = useTranslation(); + + return { + copyData: (data: string, title: string = 'Copy Success', options?: UseToastOptions) => { + try { + const textarea = document.createElement('textarea'); + textarea.value = data; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + toast({ + title: title, + status: 'success', + duration: 1000, + ...options + }); + } catch (error) { + console.error(error); + toast({ + title: t('Copy Failed'), + status: 'error' + }); + } + } + }; +}; diff --git a/frontend/providers/cloudserver/src/hooks/useLoading.tsx b/frontend/providers/cloudserver/src/hooks/useLoading.tsx new file mode 100644 index 000000000..f00dac51b --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useLoading.tsx @@ -0,0 +1,34 @@ +import { useState, useCallback } from 'react'; +import { Spinner, Flex } from '@chakra-ui/react'; + +export const useLoading = (props?: { defaultLoading: boolean }) => { + const [isLoading, setIsLoading] = useState(props?.defaultLoading || false); + + const Loading = useCallback( + ({ loading, fixed = true }: { loading?: boolean; fixed?: boolean }): JSX.Element | null => { + return ( + + + + ); + }, + [isLoading] + ); + + return { + isLoading, + setIsLoading, + Loading + }; +}; diff --git a/frontend/providers/cloudserver/src/hooks/useScreen.ts b/frontend/providers/cloudserver/src/hooks/useScreen.ts new file mode 100644 index 000000000..79af2c568 --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useScreen.ts @@ -0,0 +1,25 @@ +import { useState, useEffect } from 'react'; +import { throttle } from 'lodash'; + +export function useScreen() { + const [screenWidth, setScreenWidth] = useState(1440); + + useEffect(() => { + const resize = throttle(() => { + const w = document.documentElement.clientWidth; + setScreenWidth(w); + }, 300); + window.addEventListener('resize', resize); + + const w = document.documentElement.clientWidth; + setScreenWidth(w); + + return () => { + window.removeEventListener('resize', resize); + }; + }, []); + + return { + screenWidth + }; +} diff --git a/frontend/providers/cloudserver/src/hooks/useSelectFile.tsx b/frontend/providers/cloudserver/src/hooks/useSelectFile.tsx new file mode 100644 index 000000000..9570cc642 --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useSelectFile.tsx @@ -0,0 +1,44 @@ +import React, { useRef, useCallback } from 'react'; +import { Box } from '@chakra-ui/react'; +import { useToast } from './useToast'; +import { useTranslation } from 'react-i18next'; + +export const useSelectFile = (props?: { fileType?: string; multiple?: boolean }) => { + const { t } = useTranslation(); + const { fileType = '*', multiple = false } = props || {}; + const { toast } = useToast(); + const SelectFileDom = useRef(null); + + const File = useCallback( + ({ onSelect }: { onSelect: (e: File[]) => void }) => ( + + { + if (!e.target.files || e.target.files?.length === 0) return; + if (e.target.files.length > 10) { + return toast({ + status: 'warning', + title: t('file.Select a maximum of 10 files') + }); + } + onSelect(Array.from(e.target.files)); + }} + /> + + ), + [fileType, multiple, t, toast] + ); + + const onOpen = useCallback(() => { + SelectFileDom.current && SelectFileDom.current.click(); + }, []); + + return { + File, + onOpen + }; +}; diff --git a/frontend/providers/cloudserver/src/hooks/useStore.ts b/frontend/providers/cloudserver/src/hooks/useStore.ts new file mode 100644 index 000000000..439fcef7d --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useStore.ts @@ -0,0 +1,17 @@ +import { useState, useEffect } from 'react'; + +const useStore = ( + store: (callback: (state: T) => unknown) => unknown, + callback: (state: T) => F +) => { + const result = store(callback) as F; + const [data, setData] = useState(); + + useEffect(() => { + setData(result); + }, [result]); + + return data; +}; + +export default useStore; diff --git a/frontend/providers/cloudserver/src/hooks/useToast.ts b/frontend/providers/cloudserver/src/hooks/useToast.ts new file mode 100644 index 000000000..2c04330ad --- /dev/null +++ b/frontend/providers/cloudserver/src/hooks/useToast.ts @@ -0,0 +1,13 @@ +import { useToast as uToast, UseToastOptions } from '@chakra-ui/react'; + +export const useToast = (props?: UseToastOptions) => { + const toast = uToast({ + position: 'top', + duration: 3000, + ...props + }); + + return { + toast + }; +}; diff --git a/frontend/providers/cloudserver/src/pages/404.tsx b/frontend/providers/cloudserver/src/pages/404.tsx new file mode 100644 index 000000000..6221472b6 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/404.tsx @@ -0,0 +1,13 @@ +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; + +const NonePage = () => { + const router = useRouter(); + useEffect(() => { + router.push('/cloudservers'); + }, [router]); + + return
; +}; + +export default NonePage; diff --git a/frontend/providers/cloudserver/src/pages/_app.tsx b/frontend/providers/cloudserver/src/pages/_app.tsx new file mode 100644 index 000000000..4383e076e --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/_app.tsx @@ -0,0 +1,142 @@ +import { theme } from '@/constants/theme'; +import { useConfirm } from '@/hooks/useConfirm'; +import useEnvStore from '@/store/env'; +import { useGlobalStore } from '@/store/global'; +import useSessionStore from '@/store/session'; +import '@/styles/reset.scss'; +import { getLangStore, setLangStore } from '@/utils/cookieUtils'; +import { ChakraProvider } from '@chakra-ui/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { throttle } from 'lodash'; +import { appWithTranslation, useTranslation } from 'next-i18next'; +import type { AppProps } from 'next/app'; +import Head from 'next/head'; +import Router, { useRouter } from 'next/router'; +import NProgress from 'nprogress'; //nprogress module +import 'nprogress/nprogress.css'; +import { useEffect, useState } from 'react'; +import 'react-day-picker/dist/style.css'; +import { EVENT_NAME } from 'sealos-desktop-sdk'; +import { createSealosApp, sealosApp } from 'sealos-desktop-sdk/app'; + +//Binding events. +Router.events.on('routeChangeStart', () => NProgress.start()); +Router.events.on('routeChangeComplete', () => NProgress.done()); +Router.events.on('routeChangeError', () => NProgress.done()); + +// Create a client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: false, + cacheTime: 0 + } + } +}); + +function App({ Component, pageProps }: AppProps) { + const router = useRouter(); + const { initSystemEnv } = useEnvStore(); + const { setToken, token } = useSessionStore(); + const { i18n } = useTranslation(); + const { setScreenWidth, setLastRoute } = useGlobalStore(); + const [refresh, setRefresh] = useState(false); + + const { openConfirm, ConfirmChild } = useConfirm({ + title: '跳转提示', + content: '该应用不允许单独使用,点击确认前往 Sealos Desktop 使用。' + }); + + useEffect(() => { + const response = createSealosApp(); + (async () => { + try { + const res = await sealosApp.getSession(); + if (res.token) { + setToken(res.token); + } + } catch (err) { + console.log('App is not running in desktop'); + if (process.env.NODE_ENV === 'development') { + setToken(process.env.NEXT_PUBLIC_MOCK_USER!); + } + } + })(); + return response; + }, []); + + // add resize event + useEffect(() => { + const resize = throttle((e: Event) => { + const documentWidth = document.documentElement.clientWidth || document.body.clientWidth; + setScreenWidth(documentWidth); + }, 200); + window.addEventListener('resize', resize); + const documentWidth = document.documentElement.clientWidth || document.body.clientWidth; + setScreenWidth(documentWidth); + + return () => { + window.removeEventListener('resize', resize); + }; + }, [setScreenWidth]); + + // init + useEffect(() => { + const changeI18n = async (data: any) => { + const lastLang = getLangStore(); + const newLang = data.currentLanguage; + if (lastLang !== newLang) { + i18n?.changeLanguage?.(newLang); + setLangStore(newLang); + setRefresh((state) => !state); + } + }; + + (async () => { + try { + const lang = await sealosApp.getLanguage(); + changeI18n({ + currentLanguage: lang.lng + }); + } catch (error) { + changeI18n({ + currentLanguage: 'zh' + }); + } + })(); + + return sealosApp?.addAppEventListen(EVENT_NAME.CHANGE_I18N, changeI18n); + }, []); + + // record route + useEffect(() => { + return () => { + setLastRoute(router.asPath); + }; + }, [router.pathname]); + + useEffect(() => { + const lang = getLangStore() || 'zh'; + i18n?.changeLanguage?.(lang); + }, [refresh, router.asPath]); + + return ( + <> + + Cloud Virtual Machine + + + + + + + + + + + + ); +} + +export default appWithTranslation(App); diff --git a/frontend/providers/cloudserver/src/pages/_document.tsx b/frontend/providers/cloudserver/src/pages/_document.tsx new file mode 100644 index 000000000..5cb7e9e66 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/_document.tsx @@ -0,0 +1,21 @@ +import { Head, Html, Main, NextScript } from 'next/document'; + +export default function Document() { + return ( + + + + + + + + + + + +
+ + + + ); +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts new file mode 100644 index 000000000..c8bbe0b8a --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts @@ -0,0 +1,46 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { CreateCloudServerPayload, EditForm } from '@/types/cloudserver'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + const form = req.body as EditForm; + + const dataDisks = form.storages + .filter(({ use }) => use === 'DataDisk') + .flatMap(({ size, amount }) => Array(amount).fill(size)); + console.log(dataDisks); + + const payload: CreateCloudServerPayload = { + virtualMachinePackageFamily: form.virtualMachinePackageFamily, + virtualMachinePackageName: form.virtualMachinePackageName, + imageId: form.systemImageId, + systemDisk: form.systemDiskSize, + dataDisks: dataDisks, + ...(form.publicIpAssigned + ? { + internetMaxBandwidthOut: form.internetMaxBandWidthOut + } + : { internetMaxBandwidthOut: 0 }), + loginPassword: form.password, + loginName: '', + metaData: {} + }; + + const { data } = await POST('/action/create', payload, { + headers: { + Authorization: req.headers.authorization + } + }); + + return jsonRes(res, { + data: data + }); + } catch (error) { + console.log(error); + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/list.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/list.ts new file mode 100644 index 000000000..dfb8bd58b --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/list.ts @@ -0,0 +1,33 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + const { page = 1, pageSize = 10 } = req.body as { + page: number; + pageSize: number; + }; + + const { data, error } = await POST( + '/action/get-virtual-machine-detail-list', + { + page, + pageSize + }, + { + headers: { + Authorization: req.headers.authorization + } + } + ); + + return jsonRes(res, { + data: data || error + }); + } catch (error: any) { + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts new file mode 100644 index 000000000..0f7b06d06 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts @@ -0,0 +1,26 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + + const { data } = await POST( + '/action/get-image-list', + {}, + { + headers: { + Authorization: req.headers.authorization + } + } + ); + + return jsonRes(res, { + data: data + }); + } catch (error) { + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts new file mode 100644 index 000000000..683444458 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts @@ -0,0 +1,26 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + + const { data, error } = await POST( + '/action/get-virtual-machine-package', + {}, + { + headers: { + Authorization: req.headers.authorization + } + } + ); + + return jsonRes(res, { + data: data || error + }); + } catch (error) { + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts new file mode 100644 index 000000000..02e595223 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts @@ -0,0 +1,44 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { CreateCloudServerPayload, EditForm } from '@/types/cloudserver'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + const form = req.body as EditForm; + + const dataDisks = form.storages + .filter(({ use }) => use === 'DataDisk') + .flatMap(({ size, amount }) => Array(amount).fill(size)); + + const payload: CreateCloudServerPayload = { + virtualMachinePackageFamily: form.virtualMachinePackageFamily, + virtualMachinePackageName: form.virtualMachinePackageName, + imageId: form.systemImageId, + systemDisk: form.systemDiskSize, + dataDisks: dataDisks, + ...(form.publicIpAssigned + ? { + internetMaxBandwidthOut: form.internetMaxBandWidthOut + } + : { internetMaxBandwidthOut: 0 }), + loginPassword: form.password, + loginName: '', + metaData: {} + }; + + const result = await POST('/action/get-price', payload, { + headers: { + Authorization: req.headers.authorization + } + }); + + return jsonRes(res, { + data: result?.data + }); + } catch (error) { + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/updateStatus.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/updateStatus.ts new file mode 100644 index 000000000..02e188092 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/updateStatus.ts @@ -0,0 +1,60 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { HandleEnum } from '@/types/cloudserver'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export type UpdateStatusPayload = { + instanceName: string; + handle: HandleEnum; +}; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + const { instanceName, handle } = req.body as UpdateStatusPayload; + + const handleToUrlMap = { + [HandleEnum.Start]: '/action/start', + [HandleEnum.Stop]: '/action/stop', + [HandleEnum.Restart]: '/action/restart', + [HandleEnum.Delete]: '/action/delete' + }; + + const url = handleToUrlMap[handle]; + console.log(url); + + if (!url) { + return jsonRes(res, { + code: 500, + message: 'There is no API corresponding to this operation' + }); + } + + const { data, error } = await POST( + url, + { + instanceName: instanceName + }, + { + headers: { + Authorization: req.headers.authorization + } + } + ); + + if (error) { + return jsonRes(res, { + code: 500, + message: error + }); + } + + return jsonRes(res, { + data: data + }); + } catch (error) { + console.log(error); + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/platform/getEnv.ts b/frontend/providers/cloudserver/src/pages/api/platform/getEnv.ts new file mode 100644 index 000000000..24dee8c5e --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/platform/getEnv.ts @@ -0,0 +1,15 @@ +import { jsonRes } from '@/services/backend/response'; +import { ApiResp } from '@/services/kubernet'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export type SystemEnvResponse = { + domain?: string; +}; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + jsonRes(res, { + data: { + domain: process.env.SEALOS_DOMAIN || 'cloud.sealos.io' + } + }); +} diff --git a/frontend/providers/cloudserver/src/pages/cloudserver/create/components/ErrorModal.tsx b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/ErrorModal.tsx new file mode 100644 index 000000000..707bf7ced --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/ErrorModal.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalBody, + ModalCloseButton, + Box +} from '@chakra-ui/react'; +import MyIcon from '@/components/Icon'; + +const ErrorModal = ({ + title, + content, + onClose +}: { + title: string; + content: string; + onClose: () => void; +}) => { + return ( + + + + + + + {title} + + + + + {content} + + + + ); +}; + +export default ErrorModal; diff --git a/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx new file mode 100644 index 000000000..5d9c9066c --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx @@ -0,0 +1,537 @@ +import { getCloudServerImage, getCloudServerType } from '@/api/cloudserver'; +import MyIcon from '@/components/Icon'; +import { MyTable, TableColumnsType } from '@/components/MyTable'; +import { CloudServerType, EditForm, StorageType } from '@/types/cloudserver'; +import { + Box, + Button, + Divider, + Flex, + FormControl, + IconButton, + Image, + Input, + Radio, + Skeleton, + Stack, + Switch, + Text, + useTheme +} from '@chakra-ui/react'; +import { MySelect, RangeInput, Tabs } from '@sealos/ui'; +import { useQuery } from '@tanstack/react-query'; +import { customAlphabet } from 'nanoid'; +import { useTranslation } from 'next-i18next'; +import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; +import { UseFormReturn, useFieldArray } from 'react-hook-form'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 4); +const nanoidUpper = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 4); +const nanoidNumber = customAlphabet('0123456789', 4); + +const Label = ({ + children, + w = 120, + ...props +}: { + children: JSX.Element; + w?: number | 'auto'; + [key: string]: any; +}) => ( + + {children} + +); + +export default function Form({ + formHook, + refresh, + + setInstanceType +}: { + formHook: UseFormReturn; + refresh: boolean; + + setInstanceType: Dispatch>; +}) { + if (!formHook) return <>; + const [clientRender, setClientRender] = useState(false); + + useEffect(() => { + setClientRender(true); + }, []); + + const { data: SystemImage } = useQuery(['getCloudServerImage'], getCloudServerImage, { + staleTime: 5 * 60 * 1000 + }); + + const { data: ServersData } = useQuery(['getCloudServerType'], getCloudServerType, { + staleTime: 5 * 60 * 1000, + onSuccess(data) { + if (data?.[0]) { + formHook.setValue('virtualMachinePackageName', data[0].virtualMachinePackageName); + setInstanceType(data?.[0]); + } + } + }); + + const theme = useTheme(); + const { t } = useTranslation(); + + const { + register, + control, + setValue, + getValues, + formState: { errors } + } = formHook; + + const { + fields: storages, + append: appendStorages, + remove: removeStorages, + update: updateStorages + } = useFieldArray({ + control, + name: 'storages' + }); + + const columns = useMemo( + () => [ + { + title: t('Type'), + key: 'Type', + render: (item: CloudServerType, rowNumber, activeId) => { + return ( + + + {item.virtualMachinePackageName} + + ); + } + }, + { + title: t('CPU'), + render: (item: CloudServerType) => { + return {item.cpu}C; + }, + key: 'cpu' + }, + { + title: t('Memory'), + render: (item: CloudServerType) => { + return {item.memory}GB; + }, + key: 'memory' + }, + { + title: t('GPU'), + dataIndex: 'gpu', + key: 'GPU' + }, + { + title: t('Reference fee'), + key: 'instancePrice', + render: (item: CloudServerType) => { + return ( + + {t('Reference fee tip', { price: item.instancePrice })}{' '} + + ); + } + } + ], + [t] + ); + + const storageColumns = useMemo( + () => [ + { + title: t('The purpose of the disk'), + key: 'disk-purpose', + render: (item: StorageType) => { + return {t(item.use)}; + } + }, + { + title: t('capacity'), + key: 'capacity', + render: (item: StorageType, rowNumber) => { + return ( + + { + register(`storages.${rowNumber}.size`, { + required: + t('The number of instances cannot be empty') || + 'The number of instances cannot be empty', + min: { + value: 20, + message: t('The minimum number of instances is 20') + }, + max: { + value: 500, + message: t('The maximum number of instances is 500') + } + }); + setValue(`storages.${rowNumber}.size`, isNaN(val) ? 20 : val); + }} + /> + GiB + + ); + } + }, + { + title: t('Amount'), + key: 'amount', + render: (item: StorageType, rowNumber) => { + return ( + + {rowNumber === 0 ? ( + + 1 + + ) : ( + { + register(`storages.${rowNumber}.amount`, { + required: + t('The number of instances cannot be empty') || + 'The number of instances cannot be empty', + min: { + value: 1, + message: t('The minimum number of instances is 20') + }, + max: { + value: 20, + message: t('The maximum number of instances is 20') + } + }); + setValue(`storages.${rowNumber}.amount`, isNaN(val) ? 1 : val); + }} + /> + )} + + ); + } + }, + { + title: t('Operate'), + key: 'operate', + render: (item, rowNumber) => { + return ( + + {rowNumber === 0 ? ( + + ) : ( + } + aria-label={'button'} + bg={'#FFF'} + onClick={() => removeStorages(rowNumber)} + /> + )} + + ); + } + } + ], + [getValues, register, removeStorages, setValue, t] + ); + + return ( + + + + + {t('Basic Config')} + + + + + + + {ServersData ? ( + { + setValue('virtualMachinePackageName', item.virtualMachinePackageName); + setValue('virtualMachinePackageFamily', item.virtualMachinePackageFamily); + setInstanceType(item); + }} + /> + ) : ( + + + + )} + + + + + + + {SystemImage && + Object.keys(SystemImage)?.map((key) => { + return ( + + ); + })} + + + { + return { + value: item.id, + label: `${item.os} | ${item.version} | ${item.architect}` + }; + }) + : [] + } + onchange={(id: string) => { + setValue('systemImageId', id); + formHook.clearErrors(); + }} + {...formHook.register('systemImageId', { + required: { value: true, message: '必须选择系统镜像' } + })} + /> + + {t('Supports changing the operating system')} + + + + + + + {clientRender && ( + + + + + )} + + + + + + + {t('Assign Public IP')} + { + setValue('publicIpAssigned', e.target.checked); + }} + /> + + + {t('publicIpAssigned tips')} + + + + + {getValues('publicIpAssigned') && ( + + + {t('BandWidth')} + + {t('Bandwidth by hour')} + + + { + register('internetMaxBandWidthOut', { + required: + t('The number of instances cannot be empty') || + 'The number of instances cannot be empty', + min: { + value: 1, + message: t('The minimum number of instances is 1') + }, + max: { + value: 100, + message: t('The maximum number of instances is 100') + } + }); + setValue('internetMaxBandWidthOut', isNaN(val) ? 1 : val); + }} + /> + Mbps + + )} + + + + + { + if (val === 'self') { + setValue('autoPassword', false); + } else { + const randomStr = nanoid() + nanoidUpper() + nanoidNumber(); + setValue('autoPassword', true); + setValue('password', randomStr); + } + }} + /> + + {getValues('autoPassword') ? ( + <> + ) : ( + + + + + + )} + + + + ); +} diff --git a/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Header.tsx b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Header.tsx new file mode 100644 index 000000000..cb0223d3b --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Header.tsx @@ -0,0 +1,165 @@ +import MyIcon from '@/components/Icon'; +import { useGlobalStore } from '@/store/global'; +import { CloudServerPrice, CloudServerType } from '@/types/cloudserver'; +import { + Box, + Button, + Center, + Divider, + Flex, + Popover, + PopoverArrow, + PopoverBody, + PopoverContent, + PopoverHeader, + PopoverTrigger, + Text +} from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; +import { Decimal } from 'decimal.js'; + +const Header = ({ + title, + applyCb, + applyBtnText, + prices, + instanceType +}: { + title: string; + applyCb: () => void; + applyBtnText: string; + prices?: CloudServerPrice; + instanceType?: CloudServerType; +}) => { + const { t } = useTranslation(); + const router = useRouter(); + const { lastRoute } = useGlobalStore(); + + const priceItemStyle = { + alignItems: 'center', + py: '4px', + px: '6px', + borderRadius: 'base', + _hover: { + bg: 'grayModern.100' + } + }; + + return ( + + router.replace(lastRoute)}> + + + {t(title)} + + + +
+ {prices ? ( + + + + + {t('Reference fee')} + + + + + ¥ + {new Decimal(prices?.diskPrice) + .plus(new Decimal(prices?.instancePrice)) + .plus(new Decimal(prices?.networkPrice)) + .toNumber()} + + + + + + {t('Configuration fee details')} + + + + + + {t('Instance')} + + ¥{prices?.instancePrice}/{t('hour')} + + + + + + {t('storage fees')} + + + ¥{prices?.diskPrice}/{t('hour')} + + + + + + {t('Public network bandwidth')} + + + ¥{prices?.networkPrice}/{t('hour')} + + + + + + {/* Billing rules */} + + {t('Billing rules')} + + + + + + + {t('Storage')} + + + {t('Reference fee disk tips', { price: instanceType?.diskPerG })} + + + + + {t('BandWidth')} + + + {t('Reference fee network tips', { + price1: instanceType?.networkSpeedUnderSpeedBoundaryPerHour, + price2: instanceType?.networkSpeedAboveSpeedBoundaryPerHour + })} + + + + + + + ) : ( + + {t('Fee inquiry in progress')} + + )} +
+ +
+ ); +}; + +export default Header; diff --git a/frontend/providers/cloudserver/src/pages/cloudserver/create/index.tsx b/frontend/providers/cloudserver/src/pages/cloudserver/create/index.tsx new file mode 100644 index 000000000..4f58bbdb8 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudserver/create/index.tsx @@ -0,0 +1,130 @@ +import { createCloudServer, getCloudServerPrice } from '@/api/cloudserver'; +import { useConfirm } from '@/hooks/useConfirm'; +import { useLoading } from '@/hooks/useLoading'; +import { useToast } from '@/hooks/useToast'; +import { useGlobalStore } from '@/store/global'; +import { CloudServerType, EditForm } from '@/types/cloudserver'; +import { serviceSideProps } from '@/utils/i18n'; +import { Box, Flex } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; +import { useCallback, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import ErrorModal from './components/ErrorModal'; +import Form from './components/Form'; +import Header from './components/Header'; + +export default function EditOrder() { + const [errorMessage, setErrorMessage] = useState(''); + const { t } = useTranslation(); + const { toast } = useToast(); + const { Loading, setIsLoading } = useLoading(); + const router = useRouter(); + const [forceUpdate, setForceUpdate] = useState(false); + const { lastRoute } = useGlobalStore(); + const [instanceType, setInstanceType] = useState(); + + const { openConfirm, ConfirmChild } = useConfirm({ + content: t('Are you sure to create a cloud host?') + }); + + // form + const formHook = useForm({ + defaultValues: { + system: 'ubuntu', + systemDiskSize: 50, + publicIpAssigned: false, + internetMaxBandWidthOut: 1, + storages: [ + { + use: 'SystemDisk', + size: 50, + amount: 1 + } + ], + virtualMachinePackageFamily: 'A', + systemImageId: '' + } + }); + + // watch form change, compute new yaml + formHook.watch((data: any) => { + setForceUpdate(!forceUpdate); + }); + + const { data: prices } = useQuery(['getCloudServerPrice', forceUpdate], () => { + const temp = formHook.getValues(); + return getCloudServerPrice(temp); + }); + + const submitSuccess = async (data: EditForm) => { + setIsLoading(true); + try { + console.log(data); + await createCloudServer(data); + toast({ + status: 'success', + title: 'success' + }); + router.push(lastRoute); + } catch (error) { + console.error(error); + setErrorMessage(JSON.stringify(error)); + } + setIsLoading(false); + }; + + const submitError = useCallback(() => { + const deepSearch = (obj: any): string => { + if (!obj || typeof obj !== 'object') return t('Submit Error'); + if (!!obj.message) { + return obj.message; + } + return deepSearch(Object.values(obj)[0]); + }; + + toast({ + title: deepSearch(formHook.formState.errors), + status: 'error', + position: 'top', + duration: 3000, + isClosable: true + }); + }, [formHook.formState.errors, t, toast]); + + return ( + +
+ formHook.handleSubmit((data) => openConfirm(() => submitSuccess(data))(), submitError)() + } + applyBtnText="Submit" + /> + +
+ + + + {!!errorMessage && ( + setErrorMessage('')} /> + )} + + ); +} + +export async function getServerSideProps(context: any) { + return { + props: { ...(await serviceSideProps(context)) } + }; +} diff --git a/frontend/providers/cloudserver/src/pages/cloudservers/components/DelModal.tsx b/frontend/providers/cloudserver/src/pages/cloudservers/components/DelModal.tsx new file mode 100644 index 000000000..05a66a169 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudservers/components/DelModal.tsx @@ -0,0 +1,103 @@ +import { + Box, + Button, + Input, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay +} from '@chakra-ui/react'; +import { useCallback, useState } from 'react'; +import { updateCloudServerStatus } from '@/api/cloudserver'; +import { HandleEnum } from '@/types/cloudserver'; +import { useMessage } from '@sealos/ui'; +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; + +const DelModal = ({ + appName, + onClose, + onSuccess +}: { + appName: string; + onClose: () => void; + onSuccess: () => void; +}) => { + const { t } = useTranslation(); + const [inputValue, setInputValue] = useState(''); + const [loading, setLoading] = useState(false); + const { message: toast } = useMessage(); + const router = useRouter(); + + const handleDelApp = useCallback(async () => { + try { + setLoading(true); + await updateCloudServerStatus({ + instanceName: appName, + handle: HandleEnum.Delete + }); + toast({ + title: `${t('success')}`, + status: 'success' + }); + onSuccess(); + onClose(); + } catch (error: any) { + toast({ + title: typeof error === 'string' ? error : error.message || '删除出现了意外', + status: 'error' + }); + console.error(error); + } + setLoading(false); + }, [appName, toast, t, onSuccess, onClose]); + + return ( + + + + {t('Deletion warning')} + + + + {t('delete cloud server tip')} + + {t('Please enter')}{' '} + + {appName} + {' '} + {t('Confirm')} + + + + setInputValue(e.target.value)} + /> + + + + + + + + ); +}; + +export default DelModal; diff --git a/frontend/providers/cloudserver/src/pages/cloudservers/components/List.tsx b/frontend/providers/cloudserver/src/pages/cloudservers/components/List.tsx new file mode 100644 index 000000000..a19669421 --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudservers/components/List.tsx @@ -0,0 +1,255 @@ +import { updateCloudServerStatus } from '@/api/cloudserver'; +import AppStatusTag from '@/components/AppStatusTag'; +import MyIcon from '@/components/Icon'; +import { CVMInstanceType, HandleEnum } from '@/types/cloudserver'; +import { formatTime } from '@/utils/tools'; +import { Box, Button, Flex, FlexProps, MenuButton } from '@chakra-ui/react'; +import { LogoutIcon, MyTable, SealosMenu, useMessage } from '@sealos/ui'; +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; +import { useCallback, useMemo, useState } from 'react'; +import { sealosApp } from 'sealos-desktop-sdk/app'; +import DelModal from './DelModal'; +import { useConfirm } from '@/hooks/useConfirm'; +import { useCopyData } from '@/hooks/useCopyData'; +import { IntermediatePhases, IntermediateStates, PhaseEnum } from '@/types/cloudserver'; + +const OrderList = ({ apps = [], refetchApps }: { apps: any[]; refetchApps: () => void }) => { + const router = useRouter(); + const { t } = useTranslation(); + const { message: toast } = useMessage(); + const { copyData } = useCopyData(); + const [delAppName, setDelAppName] = useState(''); + + const { openConfirm, ConfirmChild } = useConfirm({ + content: t('close cloud server tip') + }); + + const handleWorkOrder = useCallback( + async (id: string, method: HandleEnum) => { + try { + await updateCloudServerStatus({ + instanceName: id, + handle: method + }); + toast({ + title: `success`, + status: 'success' + }); + } catch (error: any) { + toast({ + status: 'error', + title: error?.message + }); + } + refetchApps(); + }, + [refetchApps, toast] + ); + + const columns = useMemo< + { + title: string; + dataIndex?: keyof CVMInstanceType; + key: string; + render?: (item: any) => JSX.Element; + ItemStyle?: FlexProps; + }[] + >( + () => [ + { + title: t('Name'), + key: 'name', + render: (item: CVMInstanceType) => { + return ( + + {item.instanceName} + + ); + }, + ItemStyle: { + pl: '28px' + } + }, + { + title: t('Status'), + key: 'status', + render: (item: CVMInstanceType) => + }, + { + title: t('Create Time'), + key: 'createTime', + render: (item: any) => { + return ( + {formatTime(item.createTime, 'YYYY-MM-DD HH:mm')} + ); + } + }, + { + title: t('CPU'), + key: 'CPU', + dataIndex: 'cpu' + }, + { + title: t('Memory'), + key: 'Memory', + dataIndex: 'memory' + }, + { + title: t('sshName'), + key: 'sshName', + dataIndex: 'loginName' + }, + { + title: t('sshPassword'), + key: 'sshPassword', + render: (item: CVMInstanceType) => { + return copyData(item.loginPassword)}>***********; + } + }, + { + title: t('ssh port'), + key: 'ssh port', + dataIndex: 'loginPort' + }, + { + title: t('IP Address'), + key: 'IP-address', + render: (item: CVMInstanceType) => { + return ( + + {item?.privateIpAddresses?.join(',')}(内) + {item?.publicIpAddresses?.join(',')}(公) + + ); + } + }, + + { + title: t('Operation'), + key: 'control', + render: (item: CVMInstanceType) => ( + + + + + } + menuList={[ + { + child: ( + <> + + {t('Start')} + + ), + menuItemStyle: { + isDisabled: + IntermediatePhases.includes(item.phase) || + IntermediateStates.includes(item.state) || + item.phase === PhaseEnum.Started + }, + onClick: () => handleWorkOrder(item.instanceName, HandleEnum.Start) + }, + { + child: ( + <> + + {t('terminal')} + + ), + menuItemStyle: { + isDisabled: item.phase !== PhaseEnum.Started + }, + onClick: () => { + copyData( + item.loginPassword, + t('Password copied to clipboard') || 'Password copied to clipboard', + { + duration: 2000 + } + ); + const defaultCommand = `ssh ${item.loginName}@${item.privateIpAddresses}`; + console.log(defaultCommand); + sealosApp.runEvents('openDesktopApp', { + appKey: 'system-terminal', + query: { + defaultCommand + }, + messageData: { type: 'new terminal', command: defaultCommand }, + appSize: 'maxmin' + }); + } + }, + { + child: ( + <> + + {t('ReStart')} + + ), + menuItemStyle: { + isDisabled: + IntermediatePhases.includes(item.phase) || + IntermediateStates.includes(item.state) || + item.phase !== PhaseEnum.Started + }, + onClick: () => handleWorkOrder(item.instanceName, HandleEnum.Restart) + }, + { + child: ( + <> + + {t('Stop')} + + ), + menuItemStyle: { + isDisabled: + IntermediatePhases.includes(item.phase) || + IntermediateStates.includes(item.state) || + item.phase !== PhaseEnum.Started + }, + onClick: openConfirm(() => handleWorkOrder(item.instanceName, HandleEnum.Stop)) + }, + { + child: ( + <> + + {t('Delete')} + + ), + menuItemStyle: { + _hover: { + color: 'red.600', + bg: 'rgba(17, 24, 36, 0.05)' + }, + isDisabled: + IntermediatePhases.includes(item.phase) || + IntermediateStates.includes(item.state) || + item.phase !== PhaseEnum.Stopped + }, + onClick: () => setDelAppName(item.instanceName) + } + ]} + /> + + ) + } + ], + [copyData, handleWorkOrder, openConfirm, t] + ); + + return ( + <> + + {!!delAppName && ( + setDelAppName('')} onSuccess={refetchApps} /> + )} + + + ); +}; + +export default OrderList; diff --git a/frontend/providers/cloudserver/src/pages/cloudservers/index.tsx b/frontend/providers/cloudserver/src/pages/cloudservers/index.tsx new file mode 100644 index 000000000..882c2c02c --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/cloudservers/index.tsx @@ -0,0 +1,101 @@ +import MyIcon from '@/components/Icon'; +import Pagination from '@/components/Pagination'; +import { useLoading } from '@/hooks/useLoading'; +import useStore from '@/hooks/useStore'; +import useSessionStore from '@/store/session'; +import { serviceSideProps } from '@/utils/i18n'; +import { Box, Button, Flex, Image } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { useTranslation } from 'next-i18next'; +import router from 'next/router'; +import { useState } from 'react'; +import List from './components/List'; +import { MockInstance } from '@/constants/vm'; +import { listCloudServer } from '@/api/cloudserver'; + +function Home() { + const { Loading } = useLoading(); + const { t } = useTranslation(); + const [initialized, setInitialized] = useState(false); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [startTime, setStartTime] = useState(() => { + const currentDate = new Date(); + currentDate.setMonth(currentDate.getMonth() - 1); + return currentDate; + }); + const [endTime, setEndTime] = useState(new Date()); + const user = useStore(useSessionStore, (state) => state.session?.user); + + const { data, refetch } = useQuery( + ['listCloudServer', page, pageSize], + () => + listCloudServer({ + page, + pageSize + }), + { + refetchInterval: 3000, + onSettled() { + setInitialized(true); + } + } + ); + + return ( + + + + logo + + + {t('Cloud host list')} + + + ({data?.total}) + + + + + + {MockInstance && MockInstance?.length > 0 ? ( + + + + ) : ( + + + {t('Empty List')} + + )} + + {!!data?.total && ( + + )} + + ); +} + +export async function getServerSideProps(content: any) { + return { + props: { + ...(await serviceSideProps(content)) + } + }; +} + +export default Home; diff --git a/frontend/providers/cloudserver/src/services/backend/auth.ts b/frontend/providers/cloudserver/src/services/backend/auth.ts new file mode 100644 index 000000000..4b99a0690 --- /dev/null +++ b/frontend/providers/cloudserver/src/services/backend/auth.ts @@ -0,0 +1,26 @@ +import { DesktopTokenPayload } from '@/types/user'; +import { verify } from 'jsonwebtoken'; +import type { NextApiRequest } from 'next'; +import { ERROR_ENUM } from '../error'; + +const appJwtSecret = (process.env.JWT_SECRET_APP as string) || '123456789'; + +export const verifyAccessToken = async (req: NextApiRequest) => { + if (!req.headers) return Promise.reject(ERROR_ENUM.unAuthorization); + const { authorization } = req.headers; + if (!authorization) return Promise.reject(ERROR_ENUM.unAuthorization); + return verifyDesktopToken(authorization); +}; + +export const verifyDesktopToken: (token: string) => Promise = (token) => + new Promise((resolve, reject) => { + verify(token, appJwtSecret, (err, payload) => { + if (err) { + reject(err); + } else if (!payload) { + reject('payload is null'); + } else { + resolve(payload as DesktopTokenPayload); + } + }); + }); diff --git a/frontend/providers/cloudserver/src/services/backend/kubernetes.ts b/frontend/providers/cloudserver/src/services/backend/kubernetes.ts new file mode 100644 index 000000000..02f6e133a --- /dev/null +++ b/frontend/providers/cloudserver/src/services/backend/kubernetes.ts @@ -0,0 +1,276 @@ +import * as k8s from '@kubernetes/client-node'; +import * as yaml from 'js-yaml'; + +export function K8sApiDefault(): k8s.KubeConfig { + const kc = new k8s.KubeConfig(); + kc.loadFromDefault(); + return kc; +} + +export function CheckIsInCluster(): [boolean, string] { + if ( + process.env.KUBERNETES_SERVICE_HOST !== undefined && + process.env.KUBERNETES_SERVICE_HOST !== '' && + process.env.KUBERNETES_SERVICE_PORT !== undefined && + process.env.KUBERNETES_SERVICE_PORT !== '' + ) { + return [ + true, + 'https://' + process.env.KUBERNETES_SERVICE_HOST + ':' + process.env.KUBERNETES_SERVICE_PORT + ]; + } + return [false, '']; +} + +/* init api */ +export function K8sApi(config = ''): k8s.KubeConfig { + const kc = new k8s.KubeConfig(); + config ? kc.loadFromString(config) : kc.loadFromCluster(); + + const cluster = kc.getCurrentCluster(); + + if (cluster !== null) { + let server: k8s.Cluster; + + const [inCluster, hosts] = CheckIsInCluster(); + if (inCluster && hosts !== '') { + server = { + name: cluster.name, + caData: cluster.caData, + caFile: cluster.caFile, + server: hosts, + skipTLSVerify: cluster.skipTLSVerify + }; + } else { + server = { + name: cluster.name, + caData: cluster.caData, + caFile: cluster.caFile, + server: 'https://apiserver.cluster.local:6443', + skipTLSVerify: cluster.skipTLSVerify + }; + } + kc.clusters.forEach((item, i) => { + if (item.name === cluster.name) { + kc.clusters[i] = server; + } + }); + } + + return kc; +} + +export type CRDMeta = { + group: string; // group + version: string; // version + namespace: string; // namespace + plural: string; // type +}; + +export async function createYaml( + kc: k8s.KubeConfig, + specs: k8s.KubernetesObject[] +): Promise { + const client = k8s.KubernetesObjectApi.makeApiClient(kc); + const validSpecs = specs.filter((s) => s && s.kind && s.metadata); + const created = [] as k8s.KubernetesObject[]; + + try { + for (const spec of validSpecs) { + spec.metadata = spec.metadata || {}; + spec.metadata.annotations = spec.metadata.annotations || {}; + delete spec.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration']; + spec.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration'] = + JSON.stringify(spec); + console.log('create yaml: ', spec.kind); + const response = await client.create(spec); + created.push(response.body); + } + } catch (error: any) { + console.log('create error'); + /* delete success specs */ + for (const spec of created) { + try { + console.log('delete:', spec.kind); + client.delete(spec); + } catch (error) { + error; + } + } + // console.error(error, '<=create error') + return Promise.reject(error); + } + return created; +} + +export async function updateYaml( + kc: k8s.KubeConfig, + specs: k8s.KubernetesObject[], + canCreate = false +): Promise { + const client = k8s.KubernetesObjectApi.makeApiClient(kc); + const validSpecs = specs.filter((s) => s && s.kind && s.metadata); + const created = [] as k8s.KubernetesObject[]; + + try { + for (const spec of validSpecs) { + spec.metadata = spec.metadata || {}; + spec.metadata.annotations = spec.metadata.annotations || {}; + delete spec.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration']; + spec.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration'] = + JSON.stringify(spec); + + console.log('update yaml: ', spec.kind); + const response = await client.create(spec); + created.push(response.body); + } + } catch (error: any) { + /* delete success specs */ + for (const spec of validSpecs) { + try { + console.log('delete:', spec.kind); + await client.delete(spec); + } catch (error) { + error; + } + } + if (error.body.reason === 'AlreadyExists' && canCreate) { + return updateYaml(kc, specs); + } + return Promise.reject(error); + } + return created; +} + +export async function replaceYaml( + kc: k8s.KubeConfig, + specs: k8s.KubernetesObject[] +): Promise { + const client = k8s.KubernetesObjectApi.makeApiClient(kc); + const validSpecs = specs.filter((s) => s && s.kind && s.metadata); + const succeed = [] as k8s.KubernetesObject[]; + + for (const spec of validSpecs) { + spec.metadata = spec.metadata || {}; + spec.metadata.annotations = spec.metadata.annotations || {}; + delete spec.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration']; + spec.metadata.annotations['kubectl.kubernetes.io/last-applied-configuration'] = + JSON.stringify(spec); + + try { + // @ts-ignore + const { body } = await client.read(spec); + console.log('replace yaml: ', spec.kind); + // update resource + const response = await client.replace({ + ...spec, + metadata: { + ...spec.metadata, + resourceVersion: body.metadata?.resourceVersion + } + }); + succeed.push(response.body); + } catch (e: any) { + // console.error(e?.body || e, "<=replace error") + // no yaml, create it + if (e?.body?.code && +e?.body?.code === 404) { + try { + console.log('create yaml: ', spec.kind); + const response = await client.create(spec); + succeed.push(response.body); + } catch (error: any) { + // console.error(error, '<=create error') + return Promise.reject(error); + } + } else { + return Promise.reject(e); + } + } + } + return succeed; +} + +export async function delYaml(kc: k8s.KubeConfig, specs: k8s.KubernetesObject[]) { + const client = k8s.KubernetesObjectApi.makeApiClient(kc); + const validSpecs = specs.filter((s) => s && s.kind && s.metadata); + + try { + for (const spec of validSpecs) { + console.log('delete:', spec.kind); + client.delete(spec); + } + } catch (error: any) { + // console.error(error, '<=create error') + return Promise.reject(error); + } +} + +export function GetUserDefaultNameSpace(user: string): string { + return 'ns-' + user; +} + +export async function getK8s({ kubeconfig }: { kubeconfig: string }) { + const kc = K8sApi(kubeconfig); + const kube_user = kc.getCurrentUser(); + const client = k8s.KubernetesObjectApi.makeApiClient(kc); + + if (kube_user === null) { + return Promise.reject('用户不存在'); + } + + const namespace = GetUserDefaultNameSpace(kube_user.name); + + const applyYamlList = async (yamlList: string[], type: 'create' | 'replace' | 'update') => { + // insert namespace + const formatYaml: k8s.KubernetesObject[] = yamlList + .map((item) => yaml.loadAll(item)) + .flat() + .map((item: any) => { + if (item.metadata) { + item.metadata.namespace = namespace; + } + return item; + }); + + if (type === 'create') { + return createYaml(kc, formatYaml); + } else if (type === 'replace') { + return replaceYaml(kc, formatYaml); + } else if (type === 'update') { + return updateYaml(kc, formatYaml, true); + } + return createYaml(kc, formatYaml); + }; + + const delYamlList = (yamlList: string[]) => { + // insert namespace + const formatYaml: k8s.KubernetesObject[] = yamlList + .map((item) => yaml.loadAll(item)) + .flat() + .map((item: any) => { + if (item.metadata) { + item.metadata.namespace = namespace; + } + return item; + }); + return delYaml(kc, formatYaml); + }; + + return Promise.resolve({ + kc, + apiClient: client, + k8sCore: kc.makeApiClient(k8s.CoreV1Api), + k8sApp: kc.makeApiClient(k8s.AppsV1Api), + k8sAutoscaling: kc.makeApiClient(k8s.AutoscalingV2Api), + k8sNetworkingApp: kc.makeApiClient(k8s.NetworkingV1Api), + k8sCustomObjects: kc.makeApiClient(k8s.CustomObjectsApi), + k8sEvents: kc.makeApiClient(k8s.EventsV1Api), + k8sAuth: kc.makeApiClient(k8s.RbacAuthorizationV1Api), + k8sBatch: kc.makeApiClient(k8s.BatchV1Api), + metricsClient: new k8s.Metrics(kc), + kube_user, + namespace, + applyYamlList, + delYamlList + }); +} diff --git a/frontend/providers/cloudserver/src/services/backend/response.ts b/frontend/providers/cloudserver/src/services/backend/response.ts new file mode 100644 index 000000000..e2082cf61 --- /dev/null +++ b/frontend/providers/cloudserver/src/services/backend/response.ts @@ -0,0 +1,36 @@ +import { NextApiResponse } from 'next'; +import { ERROR_RESPONSE } from '../error'; + +export const jsonRes = ( + res: NextApiResponse, + props?: { + code?: number; + message?: string; + data?: T; + error?: any; + } +) => { + const { code = 200, message = '', data = null, error } = props || {}; + + // Specified error + if (typeof error === 'string' && ERROR_RESPONSE[error]) { + return res.json(ERROR_RESPONSE[error]); + } + + // another error + let msg = message; + if ((code < 200 || code >= 400) && !message) { + msg = error?.body?.message || error?.message || '请求错误'; + if (typeof error === 'string') { + msg = error; + } + console.log('===error===\n', error?.body || error); + } + + res.json({ + code, + statusText: '', + message: msg, + data: data || error || null + }); +}; diff --git a/frontend/providers/cloudserver/src/services/db/mongodb.ts b/frontend/providers/cloudserver/src/services/db/mongodb.ts new file mode 100644 index 000000000..4002b3621 --- /dev/null +++ b/frontend/providers/cloudserver/src/services/db/mongodb.ts @@ -0,0 +1,24 @@ +import { MongoClient } from 'mongodb'; + +export async function connectToDatabase() { + if (global.mongodb) { + return global.mongodb; + } + const uri = process.env.MONGODB_URI || ''; + global.mongodb = new MongoClient(uri); + // global.mongodb = 'connecting'; + try { + global.mongodb.on('error', (err) => { + global.mongodb = null; + }); + global.mongodb.on('close', () => { + global.mongodb = null; + }); + await global.mongodb.connect(); + return global.mongodb; + } catch (error) { + console.log('error->', 'mongo connect error'); + global.mongodb = null; + return Promise.reject(error); + } +} diff --git a/frontend/providers/cloudserver/src/services/error.ts b/frontend/providers/cloudserver/src/services/error.ts new file mode 100644 index 000000000..058cfe09b --- /dev/null +++ b/frontend/providers/cloudserver/src/services/error.ts @@ -0,0 +1,19 @@ +export enum ERROR_ENUM { + unAuthorization = 'unAuthorization' +} + +export const ERROR_RESPONSE: Record< + any, + { + code: number; + statusText: string; + message: string; + data?: any; + } +> = { + [ERROR_ENUM.unAuthorization]: { + code: 403, + statusText: ERROR_ENUM.unAuthorization, + message: '凭证错误' + } +}; diff --git a/frontend/providers/cloudserver/src/services/kubernet.ts b/frontend/providers/cloudserver/src/services/kubernet.ts new file mode 100644 index 000000000..48413378f --- /dev/null +++ b/frontend/providers/cloudserver/src/services/kubernet.ts @@ -0,0 +1,50 @@ +export interface ApiResp { + code: number; + message: string; + data?: T; +} + +export const isApiResp = (x: any): x is ApiResp => + typeof x.code === 'number' && typeof x.message === 'string'; + +export const showStatus = (status: number) => { + let message = ''; + switch (status) { + case 400: + message = '请求错误(400)'; + break; + case 401: + message = '未授权,请重新登录(401)'; + break; + case 403: + message = '拒绝访问(403)'; + break; + case 404: + message = '请求出错(404)'; + break; + case 408: + message = '请求超时(408)'; + break; + case 500: + message = '服务器错误(500)'; + break; + case 501: + message = '服务未实现(501)'; + break; + case 502: + message = '网络错误(502)'; + break; + case 503: + message = '服务不可用(503)'; + break; + case 504: + message = '网络超时(504)'; + break; + case 505: + message = 'HTTP版本不受支持(505)'; + break; + default: + message = `连接出错(${status})!`; + } + return `${message},请检查网络或联系管理员!`; +}; diff --git a/frontend/providers/cloudserver/src/services/request.ts b/frontend/providers/cloudserver/src/services/request.ts new file mode 100644 index 000000000..216e37f1e --- /dev/null +++ b/frontend/providers/cloudserver/src/services/request.ts @@ -0,0 +1,95 @@ +import useSessionStore from '@/store/session'; +import axios, { + AxiosHeaders, + AxiosRequestConfig, + AxiosResponse, + InternalAxiosRequestConfig +} from 'axios'; +import type { ApiResp } from './kubernet'; +import { isApiResp, showStatus } from './kubernet'; +import { getUserToken } from '@/utils/user'; + +const request = axios.create({ + baseURL: '/', + withCredentials: true, + timeout: 60000 +}); + +// request interceptor +request.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + let _headers: AxiosHeaders = config.headers; + _headers['Authorization'] = getUserToken(); + + if (!config.headers || config.headers['Content-Type'] === '') { + _headers['Content-Type'] = 'application/json'; + } + + config.headers = _headers; + return config; + }, + (error: any) => { + error.data = {}; + error.data.msg = '服务器异常,请联系管理员!'; + return Promise.resolve(error); + } +); + +// response interceptor +request.interceptors.response.use( + (response: AxiosResponse) => { + const { status, data } = response; + + if (status < 200 || status >= 300 || !isApiResp(data)) { + return Promise.reject( + status + ':' + showStatus(status) + ', ' + typeof data === 'string' ? data : String(data) + ); + } + + const apiResp = data as ApiResp; + if (apiResp.code < 200 || apiResp.code >= 400) { + return Promise.reject(apiResp); + } + + response.data = apiResp.data; + return response.data; + }, + (error: any) => { + if (axios.isCancel(error)) { + return Promise.reject('cancel request' + String(error)); + } else { + error.errMessage = '请求超时或服务器异常,请检查网络或联系管理员!'; + } + return Promise.reject(error); + } +); + +export function GET( + url: string, + data?: { [key: string]: any }, + config?: AxiosRequestConfig +): Promise { + return request.get(url, { + params: data, + ...config + }); +} + +export function POST( + url: string, + data?: { [key: string]: any }, + config?: AxiosRequestConfig +): Promise { + return request.post(url, data, config); +} + +export function DELETE( + url: string, + data?: { [key: string]: any }, + config?: AxiosRequestConfig +): Promise { + return request.delete(url, { + params: data, + ...config + }); +} diff --git a/frontend/providers/cloudserver/src/services/requestLaf.ts b/frontend/providers/cloudserver/src/services/requestLaf.ts new file mode 100644 index 000000000..bc039ee08 --- /dev/null +++ b/frontend/providers/cloudserver/src/services/requestLaf.ts @@ -0,0 +1,80 @@ +import axios, { + AxiosHeaders, + AxiosRequestConfig, + AxiosResponse, + InternalAxiosRequestConfig +} from 'axios'; + +/** + * token from app headers improtant + * ./request.ts + */ +const request = axios.create({ + baseURL: process.env.LAF_BASE_URL, + withCredentials: true, + timeout: 60000 +}); + +// request interceptor +request.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + let _headers: AxiosHeaders = config.headers; + + if (!config.headers || config.headers['Content-Type'] === '') { + _headers['Content-Type'] = 'application/json'; + } + + config.headers = _headers; + return config; + }, + (error: any) => { + error.data = {}; + error.data.msg = '服务器异常,请联系管理员!'; + return Promise.resolve(error); + } +); + +// response interceptor +request.interceptors.response.use( + (response: AxiosResponse) => { + return response.data; + }, + (error: any) => { + if (axios.isCancel(error)) { + return Promise.reject('cancel request' + String(error)); + } else { + error.errMessage = '请求超时或服务器异常,请检查网络或联系管理员!'; + } + return Promise.reject(error); + } +); + +export function GET( + url: string, + data?: { [key: string]: any }, + config?: AxiosRequestConfig +): Promise { + return request.get(url, { + params: data, + ...config + }); +} + +export function POST( + url: string, + data?: { [key: string]: any }, + config?: AxiosRequestConfig +): Promise { + return request.post(url, data, config); +} + +export function DELETE( + url: string, + data?: { [key: string]: any }, + config?: AxiosRequestConfig +): Promise { + return request.delete(url, { + params: data, + ...config + }); +} diff --git a/frontend/providers/cloudserver/src/store/env.ts b/frontend/providers/cloudserver/src/store/env.ts new file mode 100644 index 000000000..7fb176807 --- /dev/null +++ b/frontend/providers/cloudserver/src/store/env.ts @@ -0,0 +1,32 @@ +import { getSystemEnv } from '@/api/platform'; +import { SystemEnvResponse } from '@/pages/api/platform/getEnv'; +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +export let Domain: string | undefined; + +type EnvState = { + SystemEnv: SystemEnvResponse; + initSystemEnv: () => Promise; +}; + +const useEnvStore = create()( + immer((set, get) => ({ + SystemEnv: { + domain: '', + env_storage_className: '', + migrate_file_image: '', + minio_url: '' + }, + initSystemEnv: async () => { + const data = await getSystemEnv(); + Domain = data.domain; + set((state) => { + state.SystemEnv = data; + }); + return data; + } + })) +); + +export default useEnvStore; diff --git a/frontend/providers/cloudserver/src/store/global.ts b/frontend/providers/cloudserver/src/store/global.ts new file mode 100644 index 000000000..c638165fa --- /dev/null +++ b/frontend/providers/cloudserver/src/store/global.ts @@ -0,0 +1,37 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { immer } from 'zustand/middleware/immer'; + +type State = { + screenWidth: number; + setScreenWidth: (e: number) => void; + loading: boolean; + setLoading: (val: boolean) => void; + lastRoute: string; + setLastRoute: (val: string) => void; +}; + +export const useGlobalStore = create()( + devtools( + immer((set, get) => ({ + screenWidth: 1440, + setScreenWidth(e: number) { + set((state) => { + state.screenWidth = e; + }); + }, + loading: false, + setLoading(val: boolean) { + set((state) => { + state.loading = val; + }); + }, + lastRoute: '/', + setLastRoute(val) { + set((state) => { + state.lastRoute = val; + }); + } + })) + ) +); diff --git a/frontend/providers/cloudserver/src/store/session.ts b/frontend/providers/cloudserver/src/store/session.ts new file mode 100644 index 000000000..b2e9a9197 --- /dev/null +++ b/frontend/providers/cloudserver/src/store/session.ts @@ -0,0 +1,29 @@ +import type { SessionV1 } from 'sealos-desktop-sdk'; +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +type SessionState = { + session: SessionV1 | undefined; + setSession: (ss: SessionV1) => void; + delSession: () => void; + token: string; + setToken: (token: string) => void; + delToken: () => void; +}; + +const useSessionStore = create()( + immer((set, get) => ({ + session: {} as SessionV1, + token: '', + setSession: (ss: SessionV1) => set({ session: ss }), + delSession: () => { + set({ session: undefined }); + }, + setToken: (token) => set({ token: token }), + delToken: () => { + set({ token: '' }); + } + })) +); + +export default useSessionStore; diff --git a/frontend/providers/cloudserver/src/styles/reset.scss b/frontend/providers/cloudserver/src/styles/reset.scss new file mode 100644 index 000000000..7ac926de6 --- /dev/null +++ b/frontend/providers/cloudserver/src/styles/reset.scss @@ -0,0 +1,151 @@ +body, +h1, +h2, +h3, +h4, +hr, +p, +blockquote, +dl, +dt, +dd, +ul, +ol, +li, +pre, +form, +fieldset, +legend, +button, +input, +textarea, +th, +td { + margin: 0; +} +body, +button, +input, +select, +textarea { + font: 12px/1.5tahoma, arial, \5b8b\4f53; +} +// h1, h2, h3, h4, h5, h6{ font-size:100%; } +address, +cite, +dfn, +em, +var { + font-style: normal; +} + +small { + font-size: 12px; +} +ul, +ol { + list-style: none; + padding: 0; +} +a { + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +sup { + vertical-align: text-top; +} +sub { + vertical-align: text-bottom; +} +legend { + color: #000; +} +fieldset, +img { + border: 0; +} +button, +input, +select, +textarea { + font-size: 100%; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +* { + box-sizing: border-box; +} + +.icon { + width: 1em; + height: 1em; + vertical-align: -0.15em; + fill: currentColor; + overflow: hidden; +} + +input::placeholder, +textarea::placeholder { + color: var(--chakra-colors-blackAlpha-400); +} + +#__next { + height: 100%; +} + +::-webkit-scrollbar, +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track, +::-webkit-scrollbar-track { + background: transparent !important; + border-radius: 2px; +} +::-webkit-scrollbar-thumb, +::-webkit-scrollbar-thumb { + background: rgba(189, 193, 197, 1) !important; + border-radius: 2px; +} +::-webkit-scrollbar-thumb:hover, +::-webkit-scrollbar-thumb:hover { + background: rgba(189, 193, 197, 1) !important; +} + +div { + &::-webkit-scrollbar-thumb, + &::-webkit-scrollbar-thumb { + background: transparent !important; + border-radius: 2px; + transition: 1s; + } + &:hover { + &::-webkit-scrollbar-thumb, + &::-webkit-scrollbar-thumb { + background: rgba(189, 193, 197, 0.5) !important; + } + &::-webkit-scrollbar-thumb:hover, + &::-webkit-scrollbar-thumb:hover { + background: rgba(189, 193, 197, 1) !important; + } + } +} + +div.rdp { + --rdp-cell-size: 40px; + --rdp-accent-color: black; + --rdp-background-color: #f4f6f8; + /* Switch to dark colors for dark themes */ + // --rdp-accent-color-dark: #3003e1; + // --rdp-background-color-dark: #180270; + /* Outline border for focused elements */ + --rdp-outline: 2px solid var(--rdp-accent-color); + /* Outline border for focused and selected elements */ + --rdp-outline-selected: 2px solid rgba(0, 0, 0, 0.75); +} diff --git a/frontend/providers/cloudserver/src/types/adapt.ts b/frontend/providers/cloudserver/src/types/adapt.ts new file mode 100644 index 000000000..5869e3e32 --- /dev/null +++ b/frontend/providers/cloudserver/src/types/adapt.ts @@ -0,0 +1,15 @@ +import { CVMInstanceType, PhaseEnum, StateEnum } from './cloudserver'; + +export default function adaptCloudServerListItem(item: CVMInstanceType): CVMInstanceType { + const status = + item.state === StateEnum.Changing + ? PhaseEnum.Changing + : item.state === StateEnum.Restarting + ? PhaseEnum.Restarting + : item.phase; + + return { + ...item, + phase: status + }; +} diff --git a/frontend/providers/cloudserver/src/types/cloudserver.ts b/frontend/providers/cloudserver/src/types/cloudserver.ts new file mode 100644 index 000000000..596abb6ed --- /dev/null +++ b/frontend/providers/cloudserver/src/types/cloudserver.ts @@ -0,0 +1,140 @@ +export type EditForm = { + system: string; + systemImageId: string; + systemDiskSize: number; + publicIpAssigned: boolean; + internetMaxBandWidthOut: number; + password: string; + autoPassword: boolean; + storages: StorageType[]; + virtualMachinePackageFamily: string; + virtualMachinePackageName: string; +}; + +export type StorageType = { + type?: string; + size: number; + amount: number; + use: string; +}; + +export type CloudServerType = { + cpu?: number; + memory?: number; + gpu?: number; + virtualMachinePackageFamily: string; + virtualMachinePackageName: string; + instancePrice: number; + diskPerG: number; + networkSpeedBoundary: number; + networkSpeedUnderSpeedBoundaryPerHour: number; + networkSpeedAboveSpeedBoundaryPerHour: number; + status: CloudServerStatus; +}; + +export enum CloudServerStatus { + Available = 'available', + Unavailable = 'unavailable' +} + +export interface OperatingSystem { + id: string; + os: string; + version: string; + architect: string; + img?: string; +} + +export interface OperatingSystems { + [key: string]: { + images: OperatingSystem[]; + url: string; + }; +} + +export enum CloudServerVendors { + Tencent = 'tencent' +} + +export enum PhaseEnum { + Creating = 'Creating', + Created = 'Created', + Starting = 'Starting', + Started = 'Started', + Stopping = 'Stopping', + Stopped = 'Stopped', + Deleting = 'Deleting', + Deleted = 'Deleted', + // status form state + Restarting = 'Restarting', + Changing = 'Changing' +} + +export enum StateEnum { + Running = 'Running', + Stopped = 'Stopped', + Restarting = 'Restarting', + Deleted = 'Deleted', + Changing = 'Changing' +} + +export const IntermediateStates: StateEnum[] = [StateEnum.Changing, StateEnum.Restarting]; + +export const IntermediatePhases: PhaseEnum[] = [ + PhaseEnum.Creating, + PhaseEnum.Starting, + PhaseEnum.Stopping, + PhaseEnum.Deleting +]; + +export interface CVMInstanceType { + _id: string; + phase: PhaseEnum; + state: StateEnum; + namespace: string; + sealosUserId: string; + cpu: number; + memory: number; + gpu: number; + disk: number; + publicNetworkAccess: boolean; + internetMaxBandwidthOut: number | null; + imageId: string; + instanceName: string; + cloudProvider: string; + createTime: string; + updateTime: string; + instanceId: string; + publicIpAddresses: string[]; + privateIpAddresses: string[]; + loginName: string; + loginPassword: string; + loginPort: string; +} + +export enum HandleEnum { + Start = 'Start', + Stop = 'Stop', + Restart = 'Restart', + Delete = 'Delete' +} + +export type CloudServerPrice = { + instancePrice: number; + networkPrice: number; + diskPrice: number; +}; + +export type CreateCloudServerPayload = { + imageId: string; + systemDisk: number; + dataDisks: number[]; + internetMaxBandwidthOut?: number; + loginPassword: string; + virtualMachinePackageFamily: string; + virtualMachinePackageName: string; + loginName: string; + metaData: { + [key: string]: any; + }; +}; diff --git a/frontend/providers/cloudserver/src/types/index.ts b/frontend/providers/cloudserver/src/types/index.ts new file mode 100644 index 000000000..20a24318e --- /dev/null +++ b/frontend/providers/cloudserver/src/types/index.ts @@ -0,0 +1,5 @@ +import { MongoClient } from 'mongodb'; + +declare global { + var mongodb: MongoClient | null; +} diff --git a/frontend/providers/cloudserver/src/types/user.ts b/frontend/providers/cloudserver/src/types/user.ts new file mode 100644 index 000000000..0653e2f03 --- /dev/null +++ b/frontend/providers/cloudserver/src/types/user.ts @@ -0,0 +1,9 @@ +export type DesktopTokenPayload = { + regionUid: string; + userCrUid: string; + userCrName: string; + workspaceUid: string; + workspaceId: string; + userUid: string; + userId: string; +}; diff --git a/frontend/providers/cloudserver/src/utils/cookieUtils.ts b/frontend/providers/cloudserver/src/utils/cookieUtils.ts new file mode 100644 index 000000000..be69f2aaf --- /dev/null +++ b/frontend/providers/cloudserver/src/utils/cookieUtils.ts @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie'; + +export const LANG_KEY = 'NEXT_LOCALE'; + +export const setLangStore = (value: string) => { + return Cookies.set(LANG_KEY, value, { expires: 30, sameSite: 'None', secure: true }); +}; + +export const getLangStore = () => { + return Cookies.get(LANG_KEY); +}; + +export const removeLangStore = () => { + Cookies.remove(LANG_KEY); +}; diff --git a/frontend/providers/cloudserver/src/utils/crypto.ts b/frontend/providers/cloudserver/src/utils/crypto.ts new file mode 100644 index 000000000..7d78b7f3b --- /dev/null +++ b/frontend/providers/cloudserver/src/utils/crypto.ts @@ -0,0 +1,7 @@ +import * as crypto from 'crypto'; + +export const hashCrypto = (text: string): string => { + const hash = crypto.createHash('sha256'); + hash.update(text); + return hash.digest('hex'); +}; diff --git a/frontend/providers/cloudserver/src/utils/i18n.ts b/frontend/providers/cloudserver/src/utils/i18n.ts new file mode 100644 index 000000000..8e6d1544a --- /dev/null +++ b/frontend/providers/cloudserver/src/utils/i18n.ts @@ -0,0 +1,11 @@ +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import { LANG_KEY } from './cookieUtils'; + +export const serviceSideProps = (content: any) => { + return serverSideTranslations( + content.req.cookies[LANG_KEY] || 'en', + undefined, + null, + content.locales + ); +}; diff --git a/frontend/providers/cloudserver/src/utils/tools.ts b/frontend/providers/cloudserver/src/utils/tools.ts new file mode 100644 index 000000000..e15ed75a8 --- /dev/null +++ b/frontend/providers/cloudserver/src/utils/tools.ts @@ -0,0 +1,260 @@ +import { useToast } from '@/hooks/useToast'; +import { addHours, format, set, startOfDay } from 'date-fns'; +import dayjs from 'dayjs'; +import { useTranslation } from 'next-i18next'; + +export const formatTime = (time: string | number | Date, format = 'YYYY-MM-DD HH:mm:ss') => { + return dayjs(time).format(format); +}; + +export function addHoursToTime(inputTimeStr: string, hoursToAdd = 8) { + const inputTime = new Date(inputTimeStr); + inputTime.setHours(inputTime.getHours() + hoursToAdd); + return formatTime(inputTime); +} +/** + * format string to number or '' + */ +export const str2Num = (str?: string | number) => { + return !!str ? +str : 0; +}; + +/** + * add ./ in path + */ +export const pathFormat = (str: string) => { + if (str.startsWith('/')) return `.${str}`; + return `./${str}`; +}; +export const pathToNameFormat = (str: string) => { + if (!str.startsWith('/')) return str.replace(/(\/|\.)/g, '-').toLocaleLowerCase(); + return str + .substring(1) + .replace(/(\/|\.)/g, '-') + .toLocaleLowerCase(); +}; + +/** + * read a file text content + */ +export const reactLocalFileContent = (file: File) => { + return new Promise((resolve: (_: string) => void, reject) => { + const reader = new FileReader(); + reader.onload = () => { + resolve(reader.result as string); + }; + reader.onerror = (err) => { + reject(err); + }; + reader.readAsText(file); + }); +}; + +/** + * str to base64 + */ +export const strToBase64 = (str: string) => { + try { + const base64 = window.btoa(str); + return base64; + } catch (error) { + console.log(error); + } + return ''; +}; + +/** + * cpu format + */ +export const cpuFormatToM = (cpu = '0') => { + if (!cpu || cpu === '0') { + return 0; + } + let value = parseFloat(cpu); + + if (/n/gi.test(cpu)) { + value = value / 1000 / 1000; + } else if (/u/gi.test(cpu)) { + value = value / 1000; + } else if (/m/gi.test(cpu)) { + value = value; + } else { + value = value * 1000; + } + if (value < 0.1) return 0; + return Number(value.toFixed(4)); +}; + +/** + * memory format + */ +export const memoryFormatToMi = (memory = '0') => { + if (!memory || memory === '0') { + return 0; + } + + let value = parseFloat(memory); + + if (/Ki/gi.test(memory)) { + value = value / 1024; + } else if (/Mi/gi.test(memory)) { + value = value; + } else if (/Gi/gi.test(memory)) { + value = value * 1024; + } else if (/Ti/gi.test(memory)) { + value = value * 1024 * 1024; + } else { + console.log('Invalid memory value'); + value = 0; + } + + return Number(value.toFixed(2)); +}; + +/** + * storage format + */ +export const storageFormatToNum = (storage = '0') => { + return +`${storage.replace(/gi/i, '')}`; +}; + +/** + * print memory to Mi of Gi + */ +export const printMemory = (val: number) => { + return val >= 1024 ? `${Math.round(val / 1024)} Gi` : `${val} Mi`; +}; + +/** + * format pod createTime + */ +export const formatPodTime = (createTimeStamp: Date = new Date()) => { + const podStartTimeStamp = dayjs(createTimeStamp); + + let timeDiff = Math.floor(dayjs().diff(podStartTimeStamp) / 1000); + + const days = Math.floor(timeDiff / (24 * 60 * 60)); + timeDiff -= days * 24 * 60 * 60; + + const hours = Math.floor(timeDiff / (60 * 60)); + timeDiff -= hours * 60 * 60; + + const minutes = Math.floor(timeDiff / 60); + timeDiff -= minutes * 60; + + const seconds = timeDiff; + + if (days > 0) { + return `${days}d${hours}h`; + } + if (hours > 0) { + return `${hours}h${minutes}m`; + } + if (minutes > 0) { + return `${minutes}m${seconds}s`; + } + return `${seconds}s`; +}; + +export function downLoadBold(content: BlobPart, type: string, fileName: string) { + const blob = new Blob([content], { type }); + + const url = URL.createObjectURL(blob); + + const link = document.createElement('a'); + link.href = url; + link.download = fileName; + + link.click(); +} + +export const getErrText = (err: any, def = '') => { + const msg = typeof err === 'string' ? err : err?.message || def || ''; + msg && console.log('error =>', msg); + return msg; +}; + +export const delay = (ms: number) => + new Promise((resolve) => { + setTimeout(() => { + resolve(''); + }, ms); + }); + +export const convertCronTime = (cronTime: string, offset: 8 | -8) => { + let [minute, hour, dayOfMonth, month, dayOfWeek] = cronTime.split(' '); + + if (hour === '*') return cronTime; + + const cronDate = set(startOfDay(new Date()), { hours: +hour, minutes: +minute }); + + const newCronDate = addHours(cronDate, offset); + + minute = format(newCronDate, 'mm'); + hour = format(newCronDate, 'HH'); + + const daysOfWeek = + dayOfWeek === '*' + ? [dayOfWeek] + : dayOfWeek.split(',').map((day) => { + if (offset < 0) { + const newDay = +day + (+hour >= 16 ? -1 : 0); + return newDay === -1 ? '7' : String(newDay); + } + const newDay = +day + (+hour < 8 ? 1 : 0); + return newDay === 7 ? '0' : String(newDay); + }); + + return `${minute} ${hour} ${dayOfMonth} ${month} ${daysOfWeek.join(',')}`; +}; + +// convertBytes 1024 +export const convertBytes = (bytes: number, unit: 'kb' | 'mb' | 'gb' | 'tb') => { + switch (unit.toLowerCase()) { + case 'kb': + return bytes / 1024; + case 'mb': + return bytes / Math.pow(1024, 2); + case 'gb': + return bytes / Math.pow(1024, 3); + case 'tb': + return bytes / Math.pow(1024, 4); + default: + return bytes; + } +}; + +// formatTime second to day, hour or minute +export const formatTimeToDay = (seconds: number): { time: string; unit: string } => { + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(seconds / 3600); + const days = Math.floor(seconds / (3600 * 24)); + + if (days > 0) { + return { + unit: 'Day', + time: (seconds / (3600 * 24)).toFixed(1) + }; + } else if (hours > 0) { + return { + unit: 'Hour', + time: (seconds / 3600).toFixed(1) + }; + } else { + return { + unit: 'Start Minute', + time: (seconds / 60).toFixed(1) + }; + } +}; + +export const obj2Query = (obj: Record) => { + let str = ''; + Object.entries(obj).forEach(([key, val]) => { + if (val) { + str += `${key}=${val}&`; + } + }); + + return str.slice(0, str.length - 1); +}; diff --git a/frontend/providers/cloudserver/src/utils/user.ts b/frontend/providers/cloudserver/src/utils/user.ts new file mode 100644 index 000000000..13b70355e --- /dev/null +++ b/frontend/providers/cloudserver/src/utils/user.ts @@ -0,0 +1,16 @@ +import useSessionStore from '@/store/session'; + +// edge +export const getUserToken = () => { + let token: string = + process.env.NODE_ENV === 'development' ? process.env.NEXT_PUBLIC_MOCK_USER || '' : ''; + try { + const temp = useSessionStore.getState()?.token; + if (!token && temp) { + token = temp; + } + } catch (err) { + console.error(err); + } + return token; +}; diff --git a/frontend/providers/cloudserver/tsconfig.json b/frontend/providers/cloudserver/tsconfig.json new file mode 100644 index 000000000..0eef8192c --- /dev/null +++ b/frontend/providers/cloudserver/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": [ + "next-env.d.ts", + "src/**/*.ts", + "src/**/*.tsx" + ], + "extends": "../../tsconfig.web.json" +} \ No newline at end of file