diff --git a/README.md b/README.md index 346355d6..9ca330b7 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,17 @@ - [cm-butterfly](#cm-butterfly) - [Overview](#overview) - [Prerequisition](#prerequisition) - - [Recommend Envionment (Test Finished)](#recommend-envionment-test-finished) - - [1. Project clone from remote git repository](#1-project-clone-from-remote-git-repository) - - [2. cm-butterfly needs to run with cloud-migrator subsystems.](#2-cm-butterfly-needs-to-run-with-cloud-migrator-subsystems) - - [3.User credential registration ⭐⭐](#3user-credential-registration-) - - [4. subsystem's api host and endpoint configuration](#4-subsystems-api-host-and-endpoint-configuration) - - [5. self auth settings](#5-self-auth-settings) + - [Recommend Envionment (Test Finished)](#recommend-envionment-test-finished) - [How to Run](#how-to-run) - - [Change butterfly backend api host](#change-butterfly-backend-api-host) - - [Explore Awesome cm-butterfly](#explore-awesome-cm-butterfly) + - [1. Project clone from remote git repository](#1-project-clone-from-remote-git-repository) + - [2. CM-Butterfly needs to run with cloud-migrator subsystems.](#2-cm-butterfly-needs-to-run-with-cloud-migrator-subsystems) + - [3.CSP User credential registration ⭐⭐](#3csp-user-credential-registration-) + - [4. Subsystem's api host and endpoint configuration](#4-subsystems-api-host-and-endpoint-configuration) + - [5. Self auth settings (Optional)](#5-self-auth-settings-optional) + - [6. Configure Nginx for Backend API and Access Control](#6-configure-nginx-for-backend-api-and-access-control) + - [Update backend url nginx reverse proxy configuration](#update-backend-url-nginx-reverse-proxy-configuration) + - [Restrict aceess based on the `Origin` header](#restrict-aceess-based-on-the-origin-header) + - [7. Explore Awesome cm-butterfly](#7-explore-awesome-cm-butterfly) *** # cm-butterfly @@ -29,28 +31,42 @@ cm-butterfly is a framework that provides a GUI environment for a multi-cloud mi ## Prerequisition - -#### Recommend Envionment (Test Finished) +### Recommend Envionment (Test Finished) - Ubuntu 22.04 - Go 1.23.0 - Docker engine 25.0.0 - + +## How to Run ### 1. Project clone from remote git repository ```bash git clone https://github.com/cloud-barista/cm-butterfly.git ``` -### 2. cm-butterfly needs to run with cloud-migrator subsystems. +or if you need specific version with minimize the size + +```bash +git clone --depth 1 --branch v0.3.0 https://github.com/cloud-barista/cm-butterfly.git +``` + +### 2. CM-Butterfly needs to run with cloud-migrator subsystems. cm-butterfly requires execution on each server because it uses the open APIs of several subsystems that make up the cloud migrator project. -To execute each subsystem, you can execute it from the repository of each subsystem, or you can execute the entire platform using [cm-mayfly](https://github.com/cloud-barista/cm-mayfly), which provides execution and status at the cloud migration platform level. +To execute each subsystem, you can clone it from the repository of each subsystem. -You can check the detailed execution method by checking the README.md of each subsystem. +However, we strongly recommend using the cm-mayfly tool to run the entire platform. It is configured to fully utilize all the features of the cloud-migrator. -- [cm-spider](https://github.com/cloud-barista/cb-spider/tree/v0.9.6) (v0.9.6) +Follow the guide in the link. You can run the cloud-migrator with a simple terminal command and access the console. + +[cm-mayfly](https://github.com/cloud-barista/cm-mayfly), which provides execution and check status at the cloud migration platform level. + + +If you want to check the detailed information about each subsystem, please visit the link below. + +- [cm-spider](https://github.com/cloud-barista/cb-spider) - [cm-tumblebug](https://github.com/cloud-barista/cb-tumblebug) - [cm-honeybee](https://github.com/cloud-barista/cm-honeybee) +- [cm-damselfly](https://github.com/cloud-barista/cm-damselfly) - [cm-beetle](https://github.com/cloud-barista/cm-beetle) - [cm-grasshopper](https://github.com/cloud-barista/cm-grasshopper) - [cm-cicada](https://github.com/cloud-barista/cm-cicada) @@ -58,8 +74,10 @@ You can check the detailed execution method by checking the README.md of each su -## 3.User credential registration ⭐⭐ -In cm-butterfly, it is necessary to register user credentials for each CSP. Registered user's CSP credentials are used for tasks such as provisioning virtual machines in a remote environment during performance evaluations, or for retrieving price or cost information from CSP. +### 3.CSP User credential registration ⭐⭐ +> This step is very important, so I've marked it with stars. + +In cm-butterfly, it is necessary to register user credentials for each CSP. Registered user's CSP credentials are used for tasks such as provisioning virtual machines in CSP's remote environment while executing workflow, performnace test preperation, or for retrieving price or cost information from CSP. Among the subsystems used by cm-butterfly, `CB-TUMBLEBUG` provides a user-friendly process for registering and storing multi-cloud information. It is recommended to register user credentials using the credential registration method provided by `CB-TUMBLEBUG`. @@ -68,95 +86,75 @@ Follow the guide for initializing CB-Tumblebug to configure multi-cloud informat > 👉 [Initialize CB-Tumblebug to configure Multi-Cloud info](https://github.com/cloud-barista/cb-tumblebug?tab=readme-ov-file#3-initialize-cb-tumblebug-to-configure-multi-cloud-info) -### 4. subsystem's api host and endpoint configuration +### 4. Subsystem's api host and endpoint configuration ⭐ cm-butterfly reads the `cm-butterfly/api/conf/api.yaml` file to configure the host of the subsystem called by cm-butterfly and the API endpoint of each subsystem. You can call all the APIs configured in api.yaml through the same request format and response format, and you can call them using the operationId, which is a unique value of each API, to call the API. > 🧨NOTE🧨
-> Except when cm-butterfly and all other servers that are linked are developed in your own local environment, you must use the actual IP address, not localhost or 127.0.0.1, because they are accessed from the client's web browser. +> Except when cm-butterfly and all other servers that are linked are developed in your own local environment, you must use the actual IP address or Domain name, not localhost or 127.0.0.1, because they are accessed from the client's web browser. -1) Clone cm-butterfly project if you needed. - ```bash - git clone https://github.com/cloud-barista/cm-butterfly.git - - ``` +Modify the value of services.{subsystem-name}.baseurl. The host currently in use by default is set to the service DNS of the Docker container. If changes are necessary (if there is a IP address or domain), you need to change the corresponding value. Requests will be sent to the URL defined in api.yaml. + + ```yaml + ```yaml -2) Copy sample `api.yaml` file. - ```bash - cd cm-butterfly - cp ./api/conf/api.yaml.sample ./api/conf/api.yaml +```yaml - ``` + cb-spider: #service name + version: 0.10.0 + baseurl: http://cb-spider:1024/spider ## change this end with /spider + auth: + type: basic + username: + password: + + cb-tumblebug: + version: 0.10.0 + baseurl: http://cb-tumblebug:1323/tumblebug ## change this end with /tumblebug + auth: + type: basic + username: default + password: default + + cm-beetle: + version: 0.3.0 + baseurl: http://cm-beetle:8056/beetle ## change this end with /beetle + auth: + + # others ... +``` + -3) Modify the value of services.{subsystem-name}.baseurl. - ```yaml +### 5. Self auth settings (Optional) +By default, cm-butterfly supports one user with migration privileges. (The featrue that add and delete users are not currently provided.) - cb-spider: #service name - version: 0.9.4 - baseurl: http://localhost:1024/spider ## change this end with /spider - auth: - type: basic - username: - password: - - cb-tumblebug: - version: 0.9.12 - baseurl: http://localhost:1323/tumblebug ## change this end with /tumblebug - auth: - type: basic - username: default - password: default +When the application starts, it reads `./api/conf/authsetting.yaml`, creates `user.dat` in the same conf folder, and then reads the dat file to process user login. - cm-beetle: - version: 0.2.2 - baseurl: http://localhost:8056/beetle ## change this end with /beetle - auth: +The default user login information is as follows. - # others ... - ``` +> - userId: `cmiguser` +> - userPassword: `cmiguserPassword!` -### 5. self auth settings -By default, cm-butterfly supports one user with migration privileges. (The ability to add and delete users is not currently provided.) -When the application starts, it reads ./api/conf/authsetting.yaml, creates user.dat in the same conf folder, and then reads the dat file to process user login. +Before running cm-butterfly, if you want to change the value for login credentials, you need to update user login information to `authsettings.yaml` and run it. -Before running cm-butterfly, you need to add user login information to authsettings.yaml and run it. ```bash -cp ./api/conf/authsetting.yaml.sample ./api/conf/authsetting.yaml +sed -i 's/id: cmiguser/id: what-ever-you-want-id/' ./api/conf/authsetting.yaml +sed -i 's/password: cmiguserPassword!/password: what-ever-you-want-password/' ./api/conf/authsetting.yaml ``` -The default user login information is as follows. - -- userId: `cmiguser` -- userPassword: `cmiguserPassword!` - -Please change the ID and password appropriately as needed! - --- -## How to Run -You can run cm-butterfly in a container environment via docker compose. -```bash -cd scripts -docker compose up -d - - ⠴ Network scripts_cm-butterfly-network Created 2.6s - ⠼ Volume "scripts_cm-butterfly-db" Created 2.5s - ✔ Container cm-butterfly-db Started 1.8s - ✔ Container cm-butterfly-api Started 1.2s - ✔ Container cm-butterfly-front Started 1.4s -``` - - -### Change butterfly backend api host -The front of cm-butterfly includes a web server, nginx. It uses nginx's reverse proxy to make http calls to the backend API. +### 6. Configure Nginx for Backend API and Access Control +#### Update backend url nginx reverse proxy configuration +The frontend of cm-butterfly includes a web server, `nginx`. It uses nginx's reverse proxy to make http calls to the backend API. If the API server is not running in the same container environment but is running remotely, you can modify the web server configuration in `./front/nginx.conf` to specify a reverse proxy for the backend host. @@ -164,7 +162,7 @@ Currently, the container name defined in docker compose is set to DNS using http ```text # other configuration - proxy_pass + proxy_pass ``` Here's a simple terminal command for you, just run it from the root of your project. @@ -173,9 +171,23 @@ sed -i 's|proxy_pass http://cm-butterfly-api:4000;|proxy_pass https://whatever.h ``` +#### Restrict aceess based on the `Origin` header + +In `./front/nginx.conf`, there is the commented part. + +```text +# if ($http_origin != "http://localhost") { +# return 403; +# } +``` + +This commented block is intended to restrict API access to requests comming only from written domain, enhancing security by preventing unauthorized origins. + +To enable this functionaliy, simply remove the `#` symbols. + --- -## Explore Awesome cm-butterfly +### 7. Explore Awesome cm-butterfly If you run it through docker compose, you can see the login page by accessing `http://localhost/auth/login`. The user credentials are registered with the default ID and password, and if you log in, you can use cm-butterfly, which supports cloud migration. diff --git a/api/conf/api.yaml b/api/conf/api.yaml index c296f7fe..738af505 100644 --- a/api/conf/api.yaml +++ b/api/conf/api.yaml @@ -2,7 +2,7 @@ cliSpecVersion: v1 # Service Definitions (Framework Server Basic informations) services: cb-spider: #service name - version: 0.9.8 + version: 0.10.0 baseurl: http://cb-spider:1024/spider # baseurl is Scheme + Host + Base Path auth: #If you need an authentication method, describe the type and username and password in the sub type: basic @@ -10,7 +10,7 @@ services: password: cb-tumblebug: - version: 0.9.22 + version: 0.10.0 baseurl: http://cb-tumblebug:1323/tumblebug auth: type: basic @@ -18,7 +18,7 @@ services: password: default cm-beetle: - version: 0.2.3 + version: 0.3.0 baseurl: http://cm-beetle:8056/beetle auth: type: basic @@ -26,27 +26,27 @@ services: password: default cm-ant: - version: 0.2.5 + version: 0.3.0 baseurl: http://cm-ant:8880/ant auth: cm-honeybee: - version: 0.2.9 + version: 0.3.0 baseurl: http://cm-honeybee:8081/honeybee auth: cm-cicada: - version: 0.2.2 + version: 0.3.0 baseurl: http://cm-cicada:8083/cicada auth: cm-grasshopper: - version: main + version: 0.3.0 baseurl: http://cm-grasshopper:8084/grasshopper auth: cm-damselfly: - version: 0.2.0 + version: 0.3.0 baseurl: http://cm-damselfly:8088/damselfly auth: type: basic @@ -1407,6 +1407,22 @@ serviceActions: description: "Create or update a label for a resource identified by its uid" cm-beetle: + Deleteuser: + method: delete + resourcePath: /sample/users/{id} + description: "Delete a user with the given information." + Patchuser: + method: patch + resourcePath: /sample/users/{id} + description: "Patch a user with the given information." + Getuser: + method: get + resourcePath: /sample/users/{id} + description: "Get information of a user with a specific ID." + Updateuser: + method: put + resourcePath: /sample/users/{id} + description: "Update a user with the given information." Testtracing: method: get resourcePath: /test/tracing @@ -1415,6 +1431,10 @@ serviceActions: method: get resourcePath: /httpVersion description: "Checks and logs the HTTP version of the incoming request to the server console." + Listinfra: + method: get + resourcePath: /migration/ns/{nsId}/mci + description: "Get the migrated multi-cloud infrastructure (MCI)" Migrateinfra: method: post resourcePath: /migration/ns/{nsId}/mci @@ -1443,96 +1463,89 @@ serviceActions: method: post resourcePath: /sample/users description: "Create a new user with the given information." - Getuser: - method: get - resourcePath: /sample/users/{id} - description: "Get information of a user with a specific ID." - Updateuser: - method: put - resourcePath: /sample/users/{id} - description: "Update a user with the given information." - Deleteuser: - method: delete - resourcePath: /sample/users/{id} - description: "Delete a user with the given information." - Patchuser: - method: patch - resourcePath: /sample/users/{id} - description: "Patch a user with the given information." + cm-ant: - Getestimatecost: + Getloadtestexecutionstate: method: get - resourcePath: /api/v1/cost/estimate - description: "Fetch estimated cost details based on provider, region, instance type, and resource specifications. Pagination support is provided through `Page` and `Size` parameters." - Updateandgetestimatecost: + resourcePath: /api/v1/load/tests/state/{loadTestKey} + description: "Retrieve a load test execution state by load test key." + Installloadgenerator: method: post - resourcePath: /api/v1/cost/estimate - description: "Update the estimate cost based on provided specifications and retrieve the updated cost estimation. Required fields for each specification include `ProviderName`, `RegionName`, and `InstanceType`. Specifications can also be provided in a formatted string using `+` delimiter." + resourcePath: /api/v1/load/generators + description: "Install a load generator either locally or remotely." + Getallloadgeneratorinstallinfo: + method: get + resourcePath: /api/v1/load/generators + description: "Retrieve a list of all installed load generators with pagination support." Getallmonitoringagentinfos: method: get - resourcePath: /api/v1/load/monitoring/agents + resourcePath: /api/v1/load/monitoring/agent description: "Retrieve monitoring agent information based on specified criteria." + Getallloadtestexecutioninfos: + method: get + resourcePath: /api/v1/load/tests/infos + description: "Retrieve a list of all load test execution information with pagination support." + Getloadtestmetrics: + method: get + resourcePath: /api/v1/load/tests/result/metrics + description: "Retrieve load test metrics based on provided parameters." + Getallloadtestexecutionstate: + method: get + resourcePath: /api/v1/load/tests/state + description: "Retrieve a list of all load test execution states with pagination support." Uninstallmonitoringagent: method: post - resourcePath: /api/v1/load/monitoring/agents/uninstall + resourcePath: /api/v1/load/monitoring/agent/uninstall description: "Uninstall monitoring agents from specified VMs or all VMs in an mci." Getloadtestresult: method: get - resourcePath: /api/v1/load/test/result + resourcePath: /api/v1/load/tests/result description: "Retrieve load test result based on provided parameters." - Runloadtest: - method: post - resourcePath: /api/v1/load/tests/run - description: "Start a load test using the provided load test configuration." - Getloadtestexecutionstate: + Getlastloadtestresult: method: get - resourcePath: /api/v1/load/tests/state/{loadTestKey} - description: "Retrieve a load test execution state by load test key." + resourcePath: /api/v1/load/tests/result/last + description: "Retrieve last load test result based on provided parameters." + Getlastloadtestmetrics: + method: get + resourcePath: /api/v1/load/tests/result/metrics/last + description: "Retrieve last load test metrics based on provided parameters." + Stoploadtest: + method: post + resourcePath: /api/v1/load/tests/stop + description: "Stop a running load test using the provided load test key." Antserverreadiness: method: get resourcePath: /readyz description: "This endpoint checks if the CB-Ant API server is ready by verifying the status of both the load service and the cost service. If either service is unavailable, it returns a 503 status indicating the server is not ready." + Updateestimateforecastcostraw: + method: post + resourcePath: /api/v1/cost/estimate/forecast/raw + description: "Update and retrieve raw forecasted cost estimates for specified cost resources and additional AWS information over the past 14 days." Uninstallloadgenerator: method: delete resourcePath: /api/v1/load/generators/{loadGeneratorInstallInfoId} description: "Uninstall a previously installed load generator." - Installmonitoringagent: - method: post - resourcePath: /api/v1/load/monitoring/agent/install - description: "Install a monitoring agent on specific mci." - Getloadtestmetrics: - method: get - resourcePath: /api/v1/load/test/metrics - description: "Retrieve load test metrics based on provided parameters." - Getallloadtestexecutioninfos: - method: get - resourcePath: /api/v1/load/tests/infos - description: "Retrieve a list of all load test execution information with pagination support." Getloadtestexecutioninfo: method: get resourcePath: /api/v1/load/tests/infos/{loadTestKey} description: "Retrieve the load test execution state information for a specific load test key." + Runloadtest: + method: post + resourcePath: /api/v1/load/tests/run + description: "Start a load test using the provided load test configuration." Getlastloadtestexecutionstate: method: get resourcePath: /api/v1/load/tests/state/last description: "Retrieve a last load test execution state by given ids." - Updateestimateforecastcostraw: - method: post - resourcePath: /api/v1/cost/estimate/forecast/raw - description: "Update and retrieve raw forecasted cost estimates for specified cost resources and additional AWS information over the past 14 days." - Getallloadgeneratorinstallinfo: + Getestimatecost: method: get - resourcePath: /api/v1/load/generators - description: "Retrieve a list of all installed load generators with pagination support." - Installloadgenerator: + resourcePath: /api/v1/cost/estimate + description: "Fetch estimated cost details based on provider, region, instance type, and resource specifications. Pagination support is provided through `Page` and `Size` parameters." + Updateandgetestimatecost: method: post - resourcePath: /api/v1/load/generators - description: "Install a load generator either locally or remotely." - Getallloadtestexecutionstate: - method: get - resourcePath: /api/v1/load/tests/state - description: "Retrieve a list of all load test execution states with pagination support." + resourcePath: /api/v1/cost/estimate + description: "Update the estimate cost based on provided specifications and retrieve the updated cost estimation. Required fields for each specification include `ProviderName`, `RegionName`, and `InstanceType`. Specifications can also be provided in a formatted string using `+` delimiter." Getestimateforecastcost: method: get resourcePath: /api/v1/cost/estimate/forecast @@ -1541,48 +1554,60 @@ serviceActions: method: post resourcePath: /api/v1/cost/estimate/forecast description: "Update and retrieve forecasted cost estimates for a specified namespace and migration configuration ID over the past 14 days." - Stoploadtest: + Installmonitoringagent: method: post - resourcePath: /api/v1/load/tests/stop - description: "Stop a running load test using the provided load test key." + resourcePath: /api/v1/load/monitoring/agent/install + description: "Install a monitoring agent on specific mci." cm-honeybee: - Health-Check-Readyz: + Import-Helm: + method: post + resourcePath: /source_group/{sgId}/connection_info/{connId}/import/helm + description: "Import the helm information." + Get-Helm-Info-Source-Group: method: get - resourcePath: /readyz - description: "Check Honeybee is ready" - Get-Software-Info: + resourcePath: /source_group/{sgId}/helm + description: "Get the helm information for all connections in the source group." + Get-Kubernetes-Info-Source-Group: method: get - resourcePath: /source_group/{sgId}/connection_info/{connId}/software - description: "Get the software information of the connection information." + resourcePath: /source_group/{sgId}/kubernetes + description: "Get the kubernetes information for all connections in the source group." + Get-Connection-Info: + method: get + resourcePath: /source_group/{sgId}/connection_info/{connId} + description: "Get the connection information." + Update-Connection-Info: + method: put + resourcePath: /source_group/{sgId}/connection_info/{connId} + description: "Update the connection information." + Delete-Connection-Info: + method: delete + resourcePath: /source_group/{sgId}/connection_info/{connId} + description: "Delete the connection information." + Get-Helm-Info: + method: get + resourcePath: /source_group/{sgId}/connection_info/{connId}/helm + description: "Get the helm information of the connection information." + Import-Kubernetes: + method: post + resourcePath: /source_group/{sgId}/connection_info/{connId}/import/kubernetes + description: "Import the kubernetes information." + Get-Infra-Info: + method: get + resourcePath: /source_group/{sgId}/connection_info/{connId}/infra + description: "Get the infra information of the connection information." + Refresh-Connection-Info-Status: + method: put + resourcePath: /source_group/{sgId}/connection_info/{connId}/refresh + description: "Refresh the connection info status." Get-Infra-Info-Source-Group-Refined: method: get resourcePath: /source_group/{sgId}/infra/refined description: "Get the refined infra information for all connections in the source group." - Get-Benchmark-Info: - method: get - resourcePath: /bench/{connId} - description: "Get the benchmark information of the connection information." - Import-Helm: - method: post - resourcePath: /source_group/{sgId}/connection_info/{connId}/import/helm - description: "Import the helm information." - Import-Infra-Source-Group: - method: post - resourcePath: /source_group/{sgId}/import/infra - description: "Import infra information for all connections in the source group." Get-Software-Info-Source-Group: method: get resourcePath: /source_group/{sgId}/software description: "Get the software information for all connections in the source group." - Stop-Benchmark: - method: post - resourcePath: /bench/{connId}/stop - description: "Stop the benchmark" - Get-Connection-Info-Directly: - method: get - resourcePath: /connection_info/{connId} - description: "Get the connection information directly." Get-Source-Group: method: get resourcePath: /source_group/{sgId} @@ -1595,6 +1620,38 @@ serviceActions: method: delete resourcePath: /source_group/{sgId} description: "Delete the source group." + Import-Infra-Source-Group: + method: post + resourcePath: /source_group/{sgId}/import/infra + description: "Import infra information for all connections in the source group." + Get-Infra-Info-Source-Group: + method: get + resourcePath: /source_group/{sgId}/infra + description: "Get the infra information for all connections in the source group." + Get-Benchmark-Info: + method: get + resourcePath: /bench/{connId} + description: "Get the benchmark information of the connection information." + Register-Target-To-Source-Group: + method: post + resourcePath: /source_group/{sgId}/target + description: "Register target information to the source group." + Refresh-Source-Group-Connection-Info-Status: + method: put + resourcePath: /source_group/{sgId}/refresh + description: "Refresh connection info status of the source group." + Get-Software-Info: + method: get + resourcePath: /source_group/{sgId}/connection_info/{connId}/software + description: "Get the software information of the connection information." + Import-Kubernetes-Source-Group: + method: post + resourcePath: /source_group/{sgId}/import/kubernetes + description: "Import kubernetes information for all connections in the source group." + Health-Check-Readyz: + method: get + resourcePath: /readyz + description: "Check Honeybee is ready" List-Connection-Info: method: get resourcePath: /source_group/{sgId}/connection_info @@ -1603,58 +1660,34 @@ serviceActions: method: post resourcePath: /source_group/{sgId}/connection_info description: "Create the connection information." - Get-Connection-Info: - method: get - resourcePath: /source_group/{sgId}/connection_info/{connId} - description: "Get the connection information." - Update-Connection-Info: - method: put - resourcePath: /source_group/{sgId}/connection_info/{connId} - description: "Update the connection information." - Delete-Connection-Info: - method: delete - resourcePath: /source_group/{sgId}/connection_info/{connId} - description: "Delete the connection information." - Get-Kubernetes-Info: - method: get - resourcePath: /source_group/{sgId}/connection_info/{connId}/kubernetes - description: "Get the kubernetes information of the connection information." - Get-Helm-Info-Source-Group: - method: get - resourcePath: /source_group/{sgId}/helm - description: "Get the helm information for all connections in the source group." - Get-Kubernetes-Info-Source-Group: - method: get - resourcePath: /source_group/{sgId}/kubernetes - description: "Get the kubernetes information for all connections in the source group." - Import-Kubernetes-Source-Group: + Import-Software: method: post - resourcePath: /source_group/{sgId}/import/kubernetes - description: "Import kubernetes information for all connections in the source group." + resourcePath: /source_group/{sgId}/connection_info/{connId}/import/software + description: "Import the software information." + Stop-Benchmark: + method: post + resourcePath: /bench/{connId}/stop + description: "Stop the benchmark" + Get-Infra-Info-Refined: + method: get + resourcePath: /source_group/{sgId}/connection_info/{connId}/infra/refined + description: "Get the refined infra information of the connection information." Import-Software-Source-Group: method: post resourcePath: /source_group/{sgId}/import/software description: "Import software information for all connections in the source group." - Get-Infra-Info-Source-Group: - method: get - resourcePath: /source_group/{sgId}/infra - description: "Get the infra information for all connections in the source group." - Refresh-Source-Group-Connection-Info-Status: - method: put - resourcePath: /source_group/{sgId}/refresh - description: "Refresh connection info status of the source group." Import-Infra: method: post resourcePath: /source_group/{sgId}/connection_info/{connId}/import/infra description: "Import the infra information." - Get-Infra-Info-Refined: + Get-Connection-Info-Directly: method: get - resourcePath: /source_group/{sgId}/connection_info/{connId}/infra/refined - description: "Get the refined infra information of the connection information." - Import-Helm-Source-Group: - method: post - resourcePath: /source_group/{sgId}/import/helm - description: "Import helm information for all connections in the source group." + resourcePath: /connection_info/{connId} + description: "Get the connection information directly." + Refresh-Connection-Info-Status-Directly: + method: put + resourcePath: /connection_info/{connId}/refresh + description: "Refresh the connection info status directly." List-Source-Group: method: get resourcePath: /source_group @@ -1663,40 +1696,52 @@ serviceActions: method: post resourcePath: /source_group description: "Register the source group." - Import-Kubernetes: + Get-Kubernetes-Info: + method: get + resourcePath: /source_group/{sgId}/connection_info/{connId}/kubernetes + description: "Get the kubernetes information of the connection information." + Import-Helm-Source-Group: method: post - resourcePath: /source_group/{sgId}/connection_info/{connId}/import/kubernetes - description: "Import the kubernetes information." + resourcePath: /source_group/{sgId}/import/helm + description: "Import helm information for all connections in the source group." Run-Benchmark-Info: method: post resourcePath: /bench/{connId}/run description: "Run the benchmark information of the connection information. If no Benchmark Agent is present on the connected server, it will be automatically installed, and the benchmark will be executed." - Get-Helm-Info: - method: get - resourcePath: /source_group/{sgId}/connection_info/{connId}/helm - description: "Get the helm information of the connection information." - Import-Software: - method: post - resourcePath: /source_group/{sgId}/connection_info/{connId}/import/software - description: "Import the software information." - Get-Infra-Info: - method: get - resourcePath: /source_group/{sgId}/connection_info/{connId}/infra - description: "Get the infra information of the connection information." - Register-Target-To-Source-Group: - method: post - resourcePath: /source_group/{sgId}/target - description: "Register target information to the source group." cm-cicada: - List-Task-Component: + Get-Task-Directly: method: get - resourcePath: /task_component - description: "Get a list of task component." - Create-Task-Component: - method: post - resourcePath: /task_component - description: "Register the task component." + resourcePath: /task/{taskId} + description: "Get the task directly." + Get-Task-Component-By-Name: + method: get + resourcePath: /task_component/name/{tcName} + description: "Get the task component by name." + Get-Task-Component: + method: get + resourcePath: /task_component/{tcId} + description: "Get the task component." + Update-Task-Component: + method: put + resourcePath: /task_component/{tcId} + description: "Update the task component." + Delete-Task-Component: + method: delete + resourcePath: /task_component/{tcId} + description: "Delete the task component." + Get-Task-Logs: + method: get + resourcePath: /workflow/{wfId}/workflowRun/{wfRunId}/task/{taskId}/taskTryNum/{taskTyNum}/logs + description: "Get the task Logs." + List-Workflow-Template: + method: get + resourcePath: /workflow_template + description: "Get a list of workflow template." + Get-Import-Errors: + method: get + resourcePath: /importErrors + description: "Get the importErrors." List-Workflow: method: get resourcePath: /workflow @@ -1709,54 +1754,58 @@ serviceActions: method: get resourcePath: /workflow/name/{wfName} description: "Get the workflow by name." + List-Task-From-Task-Group: + method: get + resourcePath: /workflow/{wfId}/task_group/{tgId}/task + description: "Get a task list from the task group." + Health-Check-Readyz: + method: get + resourcePath: /readyz + description: "Check Cicada is ready" Get-Event-Logs: method: get resourcePath: /workflow/{wfId}/eventlogs description: "Get Eventlog." + Get-Workflow-Runs: + method: get + resourcePath: /workflow/{wfId}/runs + description: "Get the task Logs." + Get-Workflowversion: + method: get + resourcePath: /workflow/{wfId}/version/{verId} + description: "Get the WorkflowVersion." Get-Task-Instances: method: get resourcePath: /workflow/{wfId}/workflowRun/{wfRunId}/taskInstances description: "Get the task Logs." - Get-Task-Directly: - method: get - resourcePath: /task/{taskId} - description: "Get the task directly." - Get-Task-Component-By-Name: + Get-Task-Group-Directly: method: get - resourcePath: /task_component/name/{tcName} - description: "Get the task component by name." - Get-Workflow-Runs: + resourcePath: /task_group/{tgId} + description: "Get the task group directly." + List-Task: method: get - resourcePath: /workflow/{wfId}/runs - description: "Get the task Logs." + resourcePath: /workflow/{wfId}/task + description: "Get a task list of the workflow." Get-Task-Group: method: get resourcePath: /workflow/{wfId}/task_group/{tgId} description: "Get the task group." - Clear-Task-Instances: - method: post - resourcePath: /workflow/{wfId}/workflowRun/{wfRunId}/task/{taskId}/clear - description: "Clear the task Instance." - Get-Task-Logs: + List-Workflowversion: method: get - resourcePath: /workflow/{wfId}/workflowRun/{wfRunId}/task/{taskId}/taskTryNum/{taskTyNum}/logs - description: "Get the task Logs." - Get-Workflow-Template-By-Name: - method: get - resourcePath: /workflow_template/name/{wfName} - description: "Get the workflow template by name." + resourcePath: /workflow/{wfId}/version + description: "Get a workflowVersion list." Get-Workflow-Template: method: get resourcePath: /workflow_template/{wftId} description: "Get the workflow template." - Get-Import-Errors: - method: get - resourcePath: /importErrors - description: "Get the importErrors." - Get-Task-Group-Directly: + List-Task-Component: method: get - resourcePath: /task_group/{tgId} - description: "Get the task group directly." + resourcePath: /task_component + description: "Get a list of task component." + Create-Task-Component: + method: post + resourcePath: /task_component + description: "Register the task component." Get-Workflow: method: get resourcePath: /workflow/{wfId} @@ -1769,72 +1818,60 @@ serviceActions: method: delete resourcePath: /workflow/{wfId} description: "Delete the workflow." - Run-Workflow: - method: post - resourcePath: /workflow/{wfId}/run - description: "Run the workflow." - List-Task: - method: get - resourcePath: /workflow/{wfId}/task - description: "Get a task list of the workflow." - List-Task-From-Task-Group: - method: get - resourcePath: /workflow/{wfId}/task_group/{tgId}/task - description: "Get a task list from the task group." - List-Workflow-Template: - method: get - resourcePath: /workflow_template - description: "Get a list of workflow template." - Health-Check-Readyz: - method: get - resourcePath: /readyz - description: "Check Cicada is ready" - Get-Task-Component: + Get-Workflow-Template-By-Name: method: get - resourcePath: /task_component/{tcId} - description: "Get the task component." - Update-Task-Component: - method: put - resourcePath: /task_component/{tcId} - description: "Update the task component." - Delete-Task-Component: - method: delete - resourcePath: /task_component/{tcId} - description: "Delete the task component." + resourcePath: /workflow_template/name/{wfName} + description: "Get the workflow template by name." Get-Task: method: get resourcePath: /workflow/{wfId}/task/{taskId} description: "Get the task." - List-Task-Group: - method: get - resourcePath: /workflow/{wfId}/task_group - description: "Get a task group list of the workflow." Get-Task-From-Task-Group: method: get resourcePath: /workflow/{wfId}/task_group/{tgId}/task/{taskId} description: "Get the task from the task group." + Clear-Task-Instances: + method: post + resourcePath: /workflow/{wfId}/workflowRun/{wfRunId}/task/{taskId}/clear + description: "Clear the task Instance." + Run-Workflow: + method: post + resourcePath: /workflow/{wfId}/run + description: "Run the workflow." + List-Task-Group: + method: get + resourcePath: /workflow/{wfId}/task_group + description: "Get a task group list of the workflow." cm-grasshopper: - Health-Check-Readyz: + Get-Migration-List: method: get - resourcePath: /readyz - description: "Check Grasshopper is ready" - Get-Execution-List: - method: post - resourcePath: /software/execution_list - description: "Get software migration execution list." - Install-Software: - method: post - resourcePath: /software/install - description: "Install pieces of software to target." + resourcePath: /software/migration_list/{sgId} + description: "Get software migration list." Register-Software: method: post resourcePath: /software/register - description: 'Register the software.

[JSON Body Example]
{"architecture":"x86_64","install_type":"ansible","match_names":["telegraf"],"name":"telegraf","os":"Ubuntu","os_version":"22.04","version":"1.0"}' + description: "Register the software." + Health-Check-Readyz: + method: get + resourcePath: /readyz + description: "Check Grasshopper is ready" + List-Software: + method: get + resourcePath: /software + description: "Get a list of connection information." Delete-Software: method: delete resourcePath: /software/{softwareId} description: "Delete the software." + Migrate-Software: + method: post + resourcePath: /software/migrate + description: "Migrate pieces of software to target." + Get-Software-Migration-Log: + method: get + resourcePath: /software/migrate/log/{executionId} + description: "Get the software migration log." cm-damselfly: GetUserModel: diff --git a/front/.env.local b/front/.env.local index 856d7886..54258825 100644 --- a/front/.env.local +++ b/front/.env.local @@ -1 +1,4 @@ -VITE_BACKEND_ENDPOINT = /api +VITE_BACKEND_ENDPOINT = '/api' +VITE_BACKEND_URL = 'http://cm-butterfly-api:4000' +VITE_PROJECT_NAME = 'MIGRATOR' +VITE_LANGUAGE = 'en' diff --git a/front/.env.sample b/front/.env.sample deleted file mode 100644 index 021d0aff..00000000 --- a/front/.env.sample +++ /dev/null @@ -1,4 +0,0 @@ -VITE_BACKEND_ENDPOINT = '/api' -VITE_BACKEND_URL = 'https://devmigapi.onecloudcon.com' -VITE_PROJECT_NAME = 'MIGRATOR' - diff --git a/front/Dockerfile b/front/Dockerfile index 58f0aca0..b8b3039f 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -12,6 +12,7 @@ ARG VITE_BACKEND_URL=http://localhost:3000 ENV VITE_BACKEND_URL=${VITE_BACKEND_URL} ENV VITE_BACKEND_ENDPOINT=/api ENV VITE_PROJECT_NAME=MIGRATOR +ENV VITE_LANGUAGE='en' RUN npm run build diff --git a/front/README.md b/front/README.md new file mode 100644 index 00000000..5bc13454 --- /dev/null +++ b/front/README.md @@ -0,0 +1,23 @@ +# 1. 프로젝트 구조 +- 폴더구조 +- store +# 2. 기술 스택, 라이브러리 + +- node 20.9.0 +- vue 2.7 + - composition api +- pinia +- mirinae + - https://github.com/cloudforet-io/mirinae +- postcss +- tailwind css +- vue-i18n +- vite.config + +# 3. API 연동 +# 4. 공통 libs, utils +# 5. 코드 스타일 Lint, Prettier 설정 + +# 6. 환경변수 + +# 7. 빌드 diff --git a/front/nginx.conf b/front/nginx.conf index 135a2784..c6a72474 100644 --- a/front/nginx.conf +++ b/front/nginx.conf @@ -15,9 +15,9 @@ server { access_log /var/log/nginx/access.log; location /api/ { - if ($http_origin != "http://localhost") { - return 403; - } + # if ($http_origin != "http://localhost") { + # return 403; + # } proxy_pass http://cm-butterfly-api:4000; proxy_set_header Host $host; diff --git a/front/package-lock.json b/front/package-lock.json index 1aaa9f19..6d16d9f9 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -13,6 +13,7 @@ "@vueuse/core": "^10.7.2", "@vueuse/integrations": "^11.0.1", "axios": "^1.7.2", + "echarts": "^5.5.1", "jwt-decode": "^4.0.0", "pinia": "^2.1.7", "postcss-loader": "^8.1.1", @@ -63,11 +64,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -75,92 +77,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -170,9 +107,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", - "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -181,13 +118,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -471,24 +407,27 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -765,9 +704,9 @@ } }, "node_modules/@types/hammerjs": { - "version": "2.0.45", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.45.tgz", - "integrity": "sha512-qkcUlZmX6c4J8q45taBKTL3p+LbITgyx7qhlPYOdOHZB7B31K0mXbP5YA7i7SgDeEGuI9MnumiKPEMrxg8j3KQ==" + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -776,18 +715,18 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", - "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", "dev": true }, "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@types/q": { @@ -1014,39 +953,39 @@ } }, "node_modules/@volar/language-core": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.4.tgz", - "integrity": "sha512-kO9k4kTLfxpg+6lq7/KAIv3m2d62IHuCL6GbVgYZTpfKvIGoAIlDxK7pFcB/eczN2+ydg/vnyaeZ6SGyZrJw2w==", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.9.tgz", + "integrity": "sha512-t++GIrUeQnKCieZdY9e+Uar2VmTqOE4Z9KcEcdSHKmKZPuqpbbWow1YKe1i3HpU2s1JqLRVM8y/n87WKXyxJAg==", "dev": true, "dependencies": { - "@volar/source-map": "2.4.4" + "@volar/source-map": "2.4.9" } }, "node_modules/@volar/source-map": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.4.tgz", - "integrity": "sha512-xG3PZqOP2haG8XG4Pg3PD1UGDAdqZg24Ru8c/qYjYAnmcj6GBR64mstx+bZux5QOyRaJK+/lNM/RnpvBD3489g==", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.9.tgz", + "integrity": "sha512-UGE+WgJwk64OcfBwBOBKIzmF+uNx4dC5GzOvaVsHbTBp/IVqeTVsGiO5CwBAt6l3vVXYbMuddG2DU8FEnBRxTg==", "dev": true }, "node_modules/@volar/typescript": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.4.tgz", - "integrity": "sha512-QQMQRVj0fVHJ3XdRKiS1LclhG0VBXdFYlyuHRQF/xLk2PuJuHNWP26MDZNvEVCvnyUQuUQhIAfylwY5TGPgc6w==", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.9.tgz", + "integrity": "sha512-Zmh3Bq8CFD6OANKYsi4vs/l7togwfjFH0kgrT12uAsDff2AJQjbEUKTVUnxmHbnbH2B9ja7Lb6Mu/Wj9wBuJlg==", "dev": true, "dependencies": { - "@volar/language-core": "2.4.4", + "@volar/language-core": "2.4.9", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.4.tgz", - "integrity": "sha512-oNwn+BAt3n9dK9uAYvI+XGlutwuTq/wfj4xCBaZCqwwVIGtD7D6ViihEbyYZrDHIHTDE3Q6oL3/hqmAyFEy9DQ==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", "dev": true, "dependencies": { "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.4", + "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" @@ -1065,13 +1004,13 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.4.tgz", - "integrity": "sha512-yP9RRs4BDLOLfldn6ah+AGCNovGjMbL9uHvhDHf5wan4dAHLnFGOkqtfE7PPe4HTXIqE7l/NILdYw53bo1C8jw==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", "dev": true, "dependencies": { - "@vue/compiler-core": "3.5.4", - "@vue/shared": "3.5.4" + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/compiler-sfc": { @@ -1365,16 +1304,16 @@ } }, "node_modules/@vue/language-core": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.6.tgz", - "integrity": "sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz", + "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==", "dev": true, "dependencies": { - "@volar/language-core": "~2.4.1", - "@vue/compiler-dom": "^3.4.0", + "@volar/language-core": "~2.4.8", + "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", - "@vue/shared": "^3.4.0", - "computeds": "^0.0.1", + "@vue/shared": "^3.5.0", + "alien-signals": "^0.2.0", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" @@ -1413,9 +1352,9 @@ } }, "node_modules/@vue/shared": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.4.tgz", - "integrity": "sha512-L2MCDD8l7yC62Te5UUyPVpmexhL9ipVnYRw9CsWfm/BGRL5FwDX4a25bcJ/OJSD3+Hx+k/a8LDKcG2AFdJV3BA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", "dev": true }, "node_modules/@vueuse/components": { @@ -1481,13 +1420,13 @@ } }, "node_modules/@vueuse/core": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.0.3.tgz", - "integrity": "sha512-RENlh64+SYA9XMExmmH1a3TPqeIuJBNNB/63GT35MZI+zpru3oMRUA6cEFr9HmGqEgUisurwGwnIieF6qu3aXw==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.2.0.tgz", + "integrity": "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA==", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "11.0.3", - "@vueuse/shared": "11.0.3", + "@vueuse/metadata": "11.2.0", + "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -1495,9 +1434,9 @@ } }, "node_modules/@vueuse/core/node_modules/@vueuse/shared": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.0.3.tgz", - "integrity": "sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.2.0.tgz", + "integrity": "sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==", "dependencies": { "vue-demi": ">=0.14.10" }, @@ -1531,12 +1470,12 @@ } }, "node_modules/@vueuse/integrations": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.0.3.tgz", - "integrity": "sha512-w6CDisaxs19S5Fd+NPPLFaA3GoX5gxuxrbTTBu0EYap7oH13w75L6C/+7e9mcoF9akhcR6GyYajwVMQEjdapJg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.2.0.tgz", + "integrity": "sha512-zGXz3dsxNHKwiD9jPMvR3DAxQEOV6VWIEYTGVSB9PNpk4pTWR+pXrHz9gvXWcP2sTk3W2oqqS6KwWDdntUvNVA==", "dependencies": { - "@vueuse/core": "11.0.3", - "@vueuse/shared": "11.0.3", + "@vueuse/core": "11.2.0", + "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -1596,9 +1535,9 @@ } }, "node_modules/@vueuse/integrations/node_modules/@vueuse/shared": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.0.3.tgz", - "integrity": "sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.2.0.tgz", + "integrity": "sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==", "dependencies": { "vue-demi": ">=0.14.10" }, @@ -1632,9 +1571,9 @@ } }, "node_modules/@vueuse/metadata": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.0.3.tgz", - "integrity": "sha512-+FtbO4SD5WpsOcQTcC0hAhNlOid6QNLzqedtquTtQ+CRNBoAt9GuV07c6KNHK1wCmlq8DFPwgiLF2rXwgSHX5Q==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.2.0.tgz", + "integrity": "sha512-L0ZmtRmNx+ZW95DmrgD6vn484gSpVeRbgpWevFKXwqqQxW9hnSi2Ppuh2BzMjnbv4aJRiIw8tQatXT9uOB23dQ==", "funding": { "url": "https://github.com/sponsors/antfu" } @@ -1676,9 +1615,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1735,6 +1674,12 @@ "ajv": "^8.0.0-beta.0" } }, + "node_modules/alien-signals": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.0.tgz", + "integrity": "sha512-StlonZhBBrsPPwrDjiPAiVTf/rolxffLxVPT60Qv/t88BZ81BvUVzHgGqEFvJ1ii8HXtm1+zU2Icr59tfWEcag==", + "dev": true + }, "node_modules/ansi-escapes": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", @@ -2017,9 +1962,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -2036,10 +1981,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -2100,9 +2045,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", + "version": "1.0.30001678", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001678.tgz", + "integrity": "sha512-RR+4U/05gNtps58PEBDZcPWTgEO2MBeoPZ96aQcjmfkBWRIDfN451fW2qyDA9/+HohLLIL5GqiMwA+IB1pWarw==", "dev": true, "funding": [ { @@ -2378,9 +2323,9 @@ } }, "node_modules/codemirror": { - "version": "5.65.17", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.17.tgz", - "integrity": "sha512-1zOsUx3lzAOu/gnMAZkQ9kpIHcPYOc9y1Fbm2UVk5UBPkdq380nhkelG0qUwm1f7wPvTbndu9ZYlug35EwAZRQ==" + "version": "5.65.18", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz", + "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==" }, "node_modules/color": { "version": "3.2.1", @@ -2446,21 +2391,15 @@ "node": ">=14" } }, - "node_modules/computeds": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", - "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -2789,9 +2728,9 @@ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "node_modules/dompurify": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz", - "integrity": "sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==" + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.7.tgz", + "integrity": "sha512-2q4bEI+coQM8f5ez7kt2xclg1XsecaV9ASJk/54vwlfRRNQfDqJz2pzQ8t0Ix/ToBpXlVjrRIx7pFC/o8itG2Q==" }, "node_modules/domutils": { "version": "1.7.0", @@ -2813,6 +2752,20 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/echarts": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.0" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -2828,9 +2781,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.19", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz", - "integrity": "sha512-kpLJJi3zxTR1U828P+LIUDZ5ohixyo68/IcYOHLqnbTPr/wdgn4i1ECvmALN9E16JPA6cvCG5UG79gVwVdEK5w==", + "version": "1.5.52", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", + "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==", "dev": true }, "node_modules/emoji-regex": { @@ -3375,6 +3328,7 @@ "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -3469,9 +3423,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz", - "integrity": "sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==", + "version": "9.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.30.0.tgz", + "integrity": "sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -3738,9 +3692,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==" }, "node_modules/fastq": { "version": "1.17.1", @@ -3791,9 +3745,9 @@ } }, "node_modules/filepond": { - "version": "4.31.3", - "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.31.3.tgz", - "integrity": "sha512-XMbPmfkYzUsdQrJJfa3fXigOgTwksUKVPzlGaA4Kwxg833+Vsr09YkKWBq5SbWJHlMAu6CmEOQo+EzjMUtJruw==" + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.32.1.tgz", + "integrity": "sha512-saY+F6VsiUomUH+w/sFO49gwL+GZG3WglSbg46ubuowSY3imE6dl1wUu48VT1NnIIeXFqRUEDt9td2QP8C6uDg==" }, "node_modules/filepond-plugin-file-validate-type": { "version": "1.2.9", @@ -3912,9 +3866,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4017,6 +3971,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuse.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", + "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -5554,9 +5518,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, "node_modules/parent-module": { @@ -5651,9 +5615,9 @@ } }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5689,9 +5653,9 @@ } }, "node_modules/pinia": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.2.tgz", - "integrity": "sha512-ja2XqFWZC36mupU4z1ZzxeTApV7DOw44cV4dhQ9sGwun+N89v/XP7+j7q6TanS1u1tdbK4r+1BUx7heMaIdagA==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.6.tgz", + "integrity": "sha512-vIsR8JkDN5Ga2vAxqOE2cJj4VtsHnzpR1Fz30kClxlh0yCHfec6uoMeM3e/ddqmwFUejK3NlrcQa/shnpyT4hA==", "dependencies": { "@vue/devtools-api": "^6.6.3", "vue-demi": "^0.14.10" @@ -5702,7 +5666,7 @@ "peerDependencies": { "@vue/composition-api": "^1.4.0", "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.3.0" + "vue": "^2.6.14 || ^3.5.11" }, "peerDependenciesMeta": { "@vue/composition-api": { @@ -5766,9 +5730,9 @@ } }, "node_modules/postcss": { - "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -5785,8 +5749,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -5876,9 +5840,9 @@ } }, "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", "dev": true, "bin": { "yaml": "bin.mjs" @@ -6018,9 +5982,9 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/pump": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.1.tgz", - "integrity": "sha512-2ynnAmUu45oUSq51AQbeugLkMSKaz8FqVpZ6ykTqzOVkzXe8u/ezkGsYrFJqKZx+D9cVxoDrSbR7CeAwxFa5cQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -6092,14 +6056,14 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -6227,9 +6191,9 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -6314,9 +6278,9 @@ } }, "node_modules/sequential-workflow-designer": { - "version": "0.24.6", - "resolved": "https://registry.npmjs.org/sequential-workflow-designer/-/sequential-workflow-designer-0.24.6.tgz", - "integrity": "sha512-zjv60NrWKYZY8ZiL+PAG3DZK3lJX/cjBkgQ/IYElsk2rwHl6+srOFWSRKx2cEfHHNueTovNQLFVsFIWRvAtVNg==", + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/sequential-workflow-designer/-/sequential-workflow-designer-0.24.8.tgz", + "integrity": "sha512-DkLwdtkP2F8AnvrICtrgzTS4TfCpDHRTpN71G3OQSL1zky5T8I5GTm7iqnMmQkwfjRi1Xx8b8e5miW687/6jYw==", "dependencies": { "sequential-workflow-model": "^0.2.0" }, @@ -6325,22 +6289,22 @@ } }, "node_modules/sequential-workflow-editor": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/sequential-workflow-editor/-/sequential-workflow-editor-0.14.0.tgz", - "integrity": "sha512-hGP8LfqOEQSez7dxE4cuFF2W7eaaIk06BeFDlj1y5MsOxaTzXbmF0MAaztG9/mEoAy3ayxX6bYPu0uPfQFHiuQ==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/sequential-workflow-editor/-/sequential-workflow-editor-0.14.4.tgz", + "integrity": "sha512-70yv6VtzWHpHPkzTccdXRxCvmr4Ph/ZCb6tEAZyXz1cmomxWwU5eu03mx8pRw8A78Z+jEANqMv0fUclPwnLIYg==", "dependencies": { - "sequential-workflow-editor-model": "^0.14.0", + "sequential-workflow-editor-model": "^0.14.4", "sequential-workflow-model": "^0.2.0" }, "peerDependencies": { - "sequential-workflow-editor-model": "^0.14.0", + "sequential-workflow-editor-model": "^0.14.4", "sequential-workflow-model": "^0.2.0" } }, "node_modules/sequential-workflow-editor-model": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/sequential-workflow-editor-model/-/sequential-workflow-editor-model-0.14.0.tgz", - "integrity": "sha512-+s5HlEoWPDI+rHhMN4bVhkWAOefHxy1rqyg6/L+BLIv3jw2A5gYcPdwaCUGbCMoupKoTz+HdaKcaJBObY/RYNg==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/sequential-workflow-editor-model/-/sequential-workflow-editor-model-0.14.4.tgz", + "integrity": "sha512-TcC+PlkOzDmx+VcwwVdDr2Qfe5Sw9/vUc5Wl70cHFQfOkxXPPrB6HiESPIrX6Lf+6pESe5mGGMBV1gCFewo5/g==", "dependencies": { "sequential-workflow-model": "^0.2.0" }, @@ -6896,9 +6860,9 @@ } }, "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -6912,9 +6876,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", - "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -6975,14 +6939,6 @@ "node": ">=0.8" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6996,9 +6952,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", "dev": true, "engines": { "node": ">=16" @@ -7014,9 +6970,9 @@ "dev": true }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, "node_modules/tsutils": { @@ -7134,9 +7090,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -7190,9 +7146,9 @@ "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -7209,8 +7165,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -7281,9 +7237,9 @@ "integrity": "sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg==" }, "node_modules/vite": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.10.tgz", - "integrity": "sha512-Dx3olBo/ODNiMVk/cA5Yft9Ws+snLOXrhLtrI3F4XLt4syz2Yg8fayZMWScPKoz12v5BUv7VEmQHnsfpY80fYw==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.11.tgz", + "integrity": "sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ==", "dev": true, "dependencies": { "esbuild": "^0.15.9", @@ -7899,13 +7855,13 @@ } }, "node_modules/vue-tsc": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.6.tgz", - "integrity": "sha512-f98dyZp5FOukcYmbFpuSCJ4Z0vHSOSmxGttZJCsFeX0M4w/Rsq0s4uKXjcSRsZqsRgQa6z7SfuO+y0HVICE57Q==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz", + "integrity": "sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==", "dev": true, "dependencies": { - "@volar/typescript": "~2.4.1", - "@vue/language-core": "2.1.6", + "@volar/typescript": "~2.4.8", + "@vue/language-core": "2.1.10", "semver": "^7.5.4" }, "bin": { @@ -8255,6 +8211,19 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zrender": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } } diff --git a/front/package.json b/front/package.json index 8a332788..86d58a1a 100644 --- a/front/package.json +++ b/front/package.json @@ -19,6 +19,7 @@ "@vueuse/core": "^10.7.2", "@vueuse/integrations": "^11.0.1", "axios": "^1.7.2", + "echarts": "^5.5.1", "jwt-decode": "^4.0.0", "pinia": "^2.1.7", "postcss-loader": "^8.1.1", diff --git a/front/src/app/i18n/index.ts b/front/src/app/i18n/index.ts index 4e9310de..7b8c61f7 100644 --- a/front/src/app/i18n/index.ts +++ b/front/src/app/i18n/index.ts @@ -3,17 +3,19 @@ import type { IVueI18n } from 'vue-i18n'; import VueI18n from 'vue-i18n'; import en from './en.json'; +import ko from './ko.json'; Vue.use(VueI18n); -const supportLanguages = ['en'] as const; +const supportLanguages = ['en', 'ko'] as const; type supportLanguage = (typeof supportLanguages)[number]; export const i18n = new VueI18n({ - locale: 'en', + locale: (import.meta.env.VITE_LANGUAGE as string) || 'en', fallbackLocale: 'en', messages: { en, + ko, }, silentTranslationWarn: true, silentFallbackWarn: true, diff --git a/front/src/app/i18n/ko.json b/front/src/app/i18n/ko.json new file mode 100644 index 00000000..f411c469 --- /dev/null +++ b/front/src/app/i18n/ko.json @@ -0,0 +1,8 @@ +{ + "COMPONENT": { + "VERTICAL_LAYOUT": { + "EXPAND": "펼칩", + "COLLAPSE": "닫힘" + } + } +} diff --git a/front/src/app/style/style.pcss b/front/src/app/style/style.pcss index f1fbe423..e4ad9619 100644 --- a/front/src/app/style/style.pcss +++ b/front/src/app/style/style.pcss @@ -125,3 +125,10 @@ pre { padding: 0 1rem; } } + +.p-toolbox{ + .outline { + outline: none; + } +} + diff --git a/front/src/app/env.d.ts b/front/src/app/type/env.d.ts similarity index 84% rename from front/src/app/env.d.ts rename to front/src/app/type/env.d.ts index 51c2c704..b7a3871b 100644 --- a/front/src/app/env.d.ts +++ b/front/src/app/type/env.d.ts @@ -1,9 +1,8 @@ - interface ImportMeta { readonly env: { VITE_BACKEND_ENDPOINT: string; VITE_BACKEND_URL: string; VITE_PROJECT_NAME: string; + VITE_LANGUAGE: string; }; } - diff --git a/front/src/entities/mci/api/index.ts b/front/src/entities/mci/api/index.ts index 2f0ecf1c..faf6395f 100644 --- a/front/src/entities/mci/api/index.ts +++ b/front/src/entities/mci/api/index.ts @@ -3,7 +3,12 @@ import { RequestBodyWrapper, useAxiosPost, } from '../../../shared/libs'; -import { IMci, MciResponseData } from '@/entities/mci/model'; +import { + ILastloadtestStateResponse, + IMci, + IRunLoadTestRequest, + MciResponseData, +} from '@/entities/mci/model'; export interface IMciRequestParams { nsId: string | null; diff --git a/front/src/entities/mci/model/stores.ts b/front/src/entities/mci/model/stores.ts index c8c2c9b0..5bf46fda 100644 --- a/front/src/entities/mci/model/stores.ts +++ b/front/src/entities/mci/model/stores.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia'; import { ref, Ref } from 'vue'; -import { IMci, IVm } from './types.ts'; +import { ILastloadtestStateResponse, IMci, IVm } from './types.ts'; const NAMESPACE = 'MCI'; @@ -19,6 +19,13 @@ export const useMCIStore = defineStore(NAMESPACE, () => { ); } + function setMci(_mci: IMci) { + const targetMci = mcis.value.find(mci => mci.uid === _mci.uid); + if (targetMci) { + Object.assign(targetMci, _mci); + } + } + function setVmsInfo(mciID: string, vm: Array) { const mci = getMciById(mciID); if (mci) { @@ -28,16 +35,32 @@ export const useMCIStore = defineStore(NAMESPACE, () => { function setVmInfo(mciID: string, vm: IVm) { const mci = getMciById(mciID); - const targetVm = mci?.vm.find(_vm => _vm.id === vm.id); + const targetVm = mci?.vm.find(_vm => _vm.uid === vm.uid); if (targetVm) { Object.assign(targetVm, vm); } } + + function assignLastLoadTestStateToVm( + mciID: string, + vmID: string, + response: ILastloadtestStateResponse, + ) { + const mci = getMciById(mciID); + if (mci) { + const vm = mci.vm.find(_vm => _vm.id === vmID); + if (vm) { + vm.lastloadtestStateResponse = response; + } + } + } return { mcis, + setMci, setMcis, getMciById, setVmsInfo, setVmInfo, + assignLastLoadTestStateToVm, }; }); diff --git a/front/src/entities/mci/model/types.ts b/front/src/entities/mci/model/types.ts index a2a5c11c..51f34362 100644 --- a/front/src/entities/mci/model/types.ts +++ b/front/src/entities/mci/model/types.ts @@ -91,6 +91,7 @@ export interface IVm { dataDiskIds: any; // Assuming dataDiskIds can be any type sshKeyId: string; cspSshKeyId: string; + lastloadtestStateResponse?: ILastloadtestStateResponse; } interface StatusCount { @@ -136,4 +137,74 @@ export interface IMci { newVmList: any; // Assuming newVmList can be any type } -// Usage example: +export interface IRunLoadTestRequest { + agentHostname: string; + collectAdditionalSystemMetrics: boolean; + httpReqs: Array<{ + bodyData: string; + hostname: string; + method: 'get' | 'post' | 'put' | 'delete'; + path: string; + port: string; + protocol: 'http' | 'https'; + }>; + installLoadGenerator: { + installLocation: 'local' | 'remote'; + }; + nsId: string; + mciId: string; + vmId: string; + testName: string; + virtualUsers: string; + duration: string; + rampUpTime: string; + rampUpSteps: string; +} + +interface LoadGeneratorServer { + additionalVmKey: string; + createdAt: string; + csp: string; + id: number; + label: string; + lat: string; + lon: string; + machineType: string; + privateIp: string; + publicIp: string; + region: string; + sshPort: string; + startTime: string; + status: string; + updatedAt: string; + username: string; + vmId: string; + zone: string; +} + +interface LoadGeneratorInstallInfo { + createdAt: string; + id: number; + installLocation: string; + installPath: string; + installType: string; + installVersion: string; + loadGeneratorServers: LoadGeneratorServer[]; + privateKeyName: string; + publicKeyName: string; + status: string; + updatedAt: string; +} + +export interface ILastloadtestStateResponse { + compileDuration: string; + createdAt: string; + executionDuration: string; + executionStatus: string; + id: number; + loadGeneratorInstallInfo: LoadGeneratorInstallInfo; + loadTestKey: string; + startAt: string; + totalExpectedExecutionSecond: number; + updatedAt: string; +} diff --git a/front/src/entities/provider/api/index.ts b/front/src/entities/provider/api/index.ts new file mode 100644 index 00000000..89d1b28d --- /dev/null +++ b/front/src/entities/provider/api/index.ts @@ -0,0 +1,33 @@ +import { + IAxiosResponse, + RequestBodyWrapper, + useAxiosPost, +} from '@/shared/libs'; +import { + IProviderResponse, + IRegionOfProviderResponse, +} from '@/entities/provider/model/types.ts'; + +const GET_PROVIDER_LIST = 'GetProviderList'; +const GET_REGION_LIST = 'GetRegions'; + +export function useGetProviderList() { + return useAxiosPost, null>( + GET_PROVIDER_LIST, + null, + ); +} + +export function useGetRegionList(provider: string | null) { + const requestBodyWrapper: Pick< + RequestBodyWrapper<{ providerName: string | null }>, + 'pathParams' + > = { + pathParams: { providerName: provider }, + }; + + return useAxiosPost< + IAxiosResponse, + Pick, 'pathParams'> + >(GET_REGION_LIST, requestBodyWrapper); +} diff --git a/front/src/entities/provider/model/store.ts b/front/src/entities/provider/model/store.ts new file mode 100644 index 00000000..e69de29b diff --git a/front/src/entities/provider/model/types.ts b/front/src/entities/provider/model/types.ts new file mode 100644 index 00000000..d9c0ff58 --- /dev/null +++ b/front/src/entities/provider/model/types.ts @@ -0,0 +1,21 @@ +export interface IProviderResponse { + output: string[]; +} + +interface Location { + display: string; + latitude: number; + longitude: number; +} + +interface Region { + description: string; + location: Location; + regionId: string; + regionName: string; + zones: string[]; +} + +export interface IRegionOfProviderResponse { + regions: Region[]; +} diff --git a/front/src/entities/recommendedModel/api/index.ts b/front/src/entities/recommendedModel/api/index.ts new file mode 100644 index 00000000..918ad652 --- /dev/null +++ b/front/src/entities/recommendedModel/api/index.ts @@ -0,0 +1,78 @@ +import { ISourceModelResponse } from '@/entities'; +import { + IAxiosResponse, + RequestBodyWrapper, + useAxiosPost, +} from '@/shared/libs'; +import { + IEsimateCostSpecResponse, + IRecommendModelResponse, +} from '@/entities/recommendedModel/model/types.ts'; + +const GET_RECOMMEND_MODEL = 'RecommendInfra'; +const GET_RECOMMEND_COST = 'Updateandgetestimatecost'; + +export function useGetRecommendModelListBySourceModel( + sourceModelInfo: ISourceModelResponse['onpremiseInfraModel'] | null, + provider: string | null, + region: string | null, +) { + const requestWrapper: Required< + Pick< + RequestBodyWrapper<{ + desiredProvider: string | null; + desiredRegion: string | null; + onpremiseInfraModel: ISourceModelResponse['onpremiseInfraModel'] | null; + }>, + 'request' + > + > = { + request: { + desiredProvider: provider, + desiredRegion: region, + onpremiseInfraModel: sourceModelInfo, + }, + }; + return useAxiosPost< + IAxiosResponse, + Required< + Pick< + RequestBodyWrapper<{ + desiredProvider: string | null; + desiredRegion: string | null; + onpremiseInfraModel: + | ISourceModelResponse['onpremiseInfraModel'] + | null; + }>, + 'request' + > + > + >(GET_RECOMMEND_MODEL, requestWrapper); +} + +interface ISpecFormat { + commonSpec: string; + commonImage: string; +} + +export function getRecommendCost(specsWithFormat: ISpecFormat[] | null) { + const requestWrapper: Required< + Pick< + RequestBodyWrapper<{ specsWithFormat: ISpecFormat[] | null }>, + 'request' + > + > = { + request: { + specsWithFormat: specsWithFormat, + }, + }; + return useAxiosPost< + IAxiosResponse, + Required< + Pick< + RequestBodyWrapper<{ specsWithFormat: ISpecFormat[] | null }>, + 'request' + > + > + >(GET_RECOMMEND_COST, requestWrapper); +} diff --git a/front/src/entities/recommendedModel/model/types.ts b/front/src/entities/recommendedModel/model/types.ts index 58637a34..89d47e43 100644 --- a/front/src/entities/recommendedModel/model/types.ts +++ b/front/src/entities/recommendedModel/model/types.ts @@ -1,26 +1,58 @@ -export interface IRecommendedModel { - name: string; - id: string; - description: string; - label: string; - spec: string; - image: string; - rootDiskType: 'default' | string; - rootDiskSize: 'default' | number; - userPassword: string; - connection: string; - estimateCost: string; -} - export type RecommendedModelTableType = | 'name' | 'id' | 'description' - | 'label' | 'spec' | 'image' - | 'rootDiskType' - | 'rootDiskSize' - | 'userPassword' - | 'connection' | 'estimateCost'; + +interface Vm { + commonImage: string; + commonSpec: string; + description: string; + label: string | null; + name: string; + subGroupSize: string; +} + +interface TargetInfra { + description: string; + installMonAgent: string; + label: string | null; + name: string; + systemLabel: string; + vm: Vm[]; +} + +export interface IRecommendModelResponse { + description: string; + status: string; + targetInfra: TargetInfra; +} + +interface EstimateCostSpecDetail { + calculatedMonthlyPrice: number; + currency: string; + id: number; + lastUpdatedAt: string; + memory: string; + originalPricePolicy: string; + osType: string; + price: string; + priceDescription: string; + pricePolicy: string; + productDescription: string; + storage: string; + unit: string; + vCpu: string; +} + +interface EstimateCostSpecResult { + estimateForecastCostSpecDetailResults: EstimateCostSpecDetail[]; +} + +export interface IEsimateCostSpecResponse { + result: { + esimateCostSpecResults: EstimateCostSpecResult[]; + }; +} diff --git a/front/src/entities/sourceConnection/api/index.ts b/front/src/entities/sourceConnection/api/index.ts index 7a135370..5f9a9a92 100644 --- a/front/src/entities/sourceConnection/api/index.ts +++ b/front/src/entities/sourceConnection/api/index.ts @@ -19,6 +19,7 @@ const COLLECT_SW = 'import-software'; const DELETE_SOURCE_CONNECTION = 'delete-connection-info'; const REFRESH_SOURCE_GROUP_CONNECTION_INFO_STATUS = 'Refresh-Source-Group-Connection-Info-Status'; +const GET_INFRA_INFO_REFINED = 'get-infra-info-refined'; export function useCreateConnectionInfo( sgId: string | null, @@ -145,7 +146,39 @@ export function useRefreshSourceGroupConnectionInfoStatus(sgId: string | null) { }; return useAxiosPost< - IAxiosResponse, + IAxiosResponse<{ message: string }>, Required, 'pathParams'>> >(REFRESH_SOURCE_GROUP_CONNECTION_INFO_STATUS, requestWrapper); } + +export function useGetInfraInfoRefined( + sgId: string | null, + connId: string | null, +) { + const requestWrapper: Required< + Pick< + RequestBodyWrapper<{ + sgId: string | null; + connId: string | null; + }>, + 'pathParams' + > + > = { + pathParams: { + sgId, + connId, + }, + }; + return useAxiosPost< + IAxiosResponse, + Required< + Pick< + RequestBodyWrapper<{ + sgId: string | null; + connId: string | null; + }>, + 'pathParams' + > + > + >(GET_INFRA_INFO_REFINED, requestWrapper); +} diff --git a/front/src/entities/sourceModels/api/index.ts b/front/src/entities/sourceModels/api/index.ts index e69de29b..2d07c1e3 100644 --- a/front/src/entities/sourceModels/api/index.ts +++ b/front/src/entities/sourceModels/api/index.ts @@ -0,0 +1,91 @@ +import { + IAxiosResponse, + RequestBodyWrapper, + useAxiosPost, +} from '@/shared/libs'; +import { ISourceConnectionResponse } from '@/entities/sourceConnection/model/types.ts'; +import { IRecommendModelResponse } from '@/entities/recommendedModel/model/types.ts'; +import { IOnpremModelPayload, ISourceModelResponse } from '@/entities'; +import { IEditWorkspaceData } from '@/entities/workspace/model/types.ts'; +import { axiosInstance } from '@/shared/libs/api/instance.ts'; + +const GET_SOURCE_MODEL_LIST = 'GetUserModel'; +const UPDATE_SOURCE_MODEL = 'UpdateOnpremmodel'; +const CREATE_ONPREMMODEL = 'CreateOnpremmodel'; +const DELETE_ONPREMMODEL = 'DeleteOnpremmodel'; + +export function useGetSourceModelList() { + const requestWrapper: Required, 'pathParams'>> = + { + pathParams: { isTargetModel: 'false' }, + }; + return useAxiosPost< + IAxiosResponse, + Required, 'pathParams'>> + >(GET_SOURCE_MODEL_LIST, requestWrapper); +} + +interface ICreateSourceModelPayload { + onpremiseInfraModel: ISourceModelResponse['onpremiseInfraModel']; + description: string; + isInitUserModel: true; + userId: string; + userModelName: string; + userModelVersion: string; +} +export function useUpdateSourceModel( + modelId: string | null, + requestData: ICreateSourceModelPayload | null, +) { + const requestBodyWrapper: Pick< + RequestBodyWrapper< + Partial<{ + id: string | null; + requestData: ICreateSourceModelPayload | null; + }> + >, + 'pathParams' | 'request' + > = { + pathParams: { + id: modelId, + }, + request: { requestData }, + }; + + return useAxiosPost< + IAxiosResponse, + Pick< + RequestBodyWrapper< + Partial<{ + id: string | null; + requestData: ICreateSourceModelPayload | null; + }> + >, + 'pathParams' | 'request' + > + >(UPDATE_SOURCE_MODEL, requestBodyWrapper); +} + +export function useCreateOnpremmodel(data: IOnpremModelPayload | null) { + const requestWrapper: Required< + Pick, 'request'> + > = { + request: data, + }; + return useAxiosPost< + IAxiosResponse, + Required, 'request'>> + >(CREATE_ONPREMMODEL, requestWrapper); +} + +export function useBulkAddWorkspaceList(modelIds: string[]) { + const promiseArr = modelIds.map(modelId => { + return axiosInstance.post(DELETE_ONPREMMODEL, { + pathParams: { + id: modelId, + }, + }); + }); + + return Promise.all(promiseArr); +} diff --git a/front/src/entities/sourceModels/model/stores.ts b/front/src/entities/sourceModels/model/stores.ts index 51b9d643..0bff1d39 100644 --- a/front/src/entities/sourceModels/model/stores.ts +++ b/front/src/entities/sourceModels/model/stores.ts @@ -1,152 +1,22 @@ import { defineStore } from 'pinia'; -import { formatDate } from '@/shared/utils'; -import type { ISourceModel } from './types'; +import { ISourceModelResponse } from './types'; +import { ref } from 'vue'; -export const dateType = - new Date().getFullYear() + - '-' + - new Date().getMonth() + - '-' + - new Date().getDate() + - ' ' + - new Date().getHours() + - ':' + - new Date().getMinutes() + - ':' + - new Date().getSeconds(); +export const useSourceModelStore = defineStore('SOURCEMODEL', () => { + const models = ref([]); -export const useSourceModelStore = defineStore('SOURCEMODEL', { - state: () => ({ - models: [ - { - id: '10001', - name: 'sourceservicename01-md01', - description: 'model1 description', - migrationType: 'Infra', - custom: 'Basic', - createdDateTime: dateType, - updatedDateTime: dateType, - modelType: 'Source', - customAndViewJSON: {}, - recommendModel: [ - { - name: 'recommendedmodelname-01', - id: '100001', - description: 'recommendedmodeldescription-01', - label: 'Rehosted vm', - spec: 'aws-ap-northeast-2-t2-small', - image: 'ubuntu 18.04', - rootDiskType: 'default', - rootDiskSize: 'default', - userPassword: 'qwerqwerqwer', - connection: 'aws-seoul-conname01', - estimateCost: '12.800/M', - }, - { - name: 'recommendedmodelname-02', - id: '100002', - description: 'recommendedmodeldescription-02', - label: 'Rehosted vm', - spec: 'aws-ap-northeast-2-t2-small', - image: 'ubuntu 18.04', - rootDiskType: 'default', - rootDiskSize: 'default', - userPassword: 'qwerqwerqwer', - connection: 'aws-seoul-conname01', - estimateCost: '12.800/M', - }, - { - name: 'recommendedmodelname-03', - id: '100003', - description: 'recommendedmodeldescription-03', - label: 'Rehosted vm', - spec: 'aws-ap-northeast-2-t2-small', - image: 'ubuntu 18.04', - rootDiskType: 'default', - rootDiskSize: 'default', - userPassword: 'qwerqwerqwer', - connection: 'aws-seoul-conname01', - estimateCost: '12.800/M', - }, - ], - }, - { - id: '10002', - name: 'sourceservicename01-md02', - description: 'model2 description', - migrationType: 'Software', - custom: 'Custom', - createdDateTime: dateType, - updatedDateTime: dateType, - modelType: 'Source', - customAndViewJSON: {}, - recommendModel: [ - { - name: 'recommendedmodelname-01', - id: '100001', - description: 'recommendedmodeldescription-01', - label: 'Rehosted vm', - spec: 'aws-ap-northeast-2-t2-small', - image: 'ubuntu 18.04', - rootDiskType: 'default', - rootDiskSize: 'default', - userPassword: 'qwerqwerqwer', - connection: 'aws-seoul-conname01', - estimateCost: '12.800/M', - }, - { - name: 'recommendedmodelname-02', - id: '100002', - description: 'recommendedmodeldescription-02', - label: 'Rehosted vm', - spec: 'aws-ap-northeast-2-t2-small', - image: 'ubuntu 18.04', - rootDiskType: 'default', - rootDiskSize: 'default', - userPassword: 'qwerqwerqwer', - connection: 'aws-seoul-conname01', - estimateCost: '12.800/M', - }, - { - name: 'recommendedmodelname-03', - id: '100003', - description: 'recommendedmodeldescription-03', - label: 'Rehosted vm', - spec: 'aws-ap-northeast-2-t2-small', - image: 'ubuntu 18.04', - rootDiskType: 'default', - rootDiskSize: 'default', - userPassword: 'qwerqwerqwer', - connection: 'aws-seoul-conname01', - estimateCost: '12.800/M', - }, - ], - }, - ] as ISourceModel[], - }), - getters: { - getModelById: state => (id: string) => { - return ( - state.models.find((model: ISourceModel) => { - return model.id === id; - }) || null - ); - }, - }, - actions: { - setModels(_models: ISourceModel[]) { - this.models = _models.map(model => ({ - id: model.id, - name: model.name, - description: model.description, - migrationType: model.migrationType, - custom: model.custom, - createdDateTime: formatDate(model.createdDateTime), - updatedDateTime: formatDate(model.updatedDateTime), - modelType: model.modelType, - customAndViewJSON: model.customAndViewJSON, - recommendModel: model.recommendModel, - })); - }, - }, + function getModels() { + return models; + } + function getSourceModelById( + modelId: string, + ): ISourceModelResponse | undefined { + return models.value?.find(el => el.id === modelId); + } + + function setSourceModel(sourceModelList: ISourceModelResponse[]) { + models.value = sourceModelList; + } + + return { getModels, getSourceModelById, setSourceModel }; }); diff --git a/front/src/entities/sourceModels/model/types.ts b/front/src/entities/sourceModels/model/types.ts index 1a2820b6..ba0b94b8 100644 --- a/front/src/entities/sourceModels/model/types.ts +++ b/front/src/entities/sourceModels/model/types.ts @@ -1,18 +1,3 @@ -import { IRecommendedModel } from '@/entities/recommendedModel/model/types'; - -export interface ISourceModel { - id: string; - name: string; - description: string; - migrationType: string; - custom: string; - createdDateTime: string | Date; - updatedDateTime: string | Date; - modelType: 'Source'; - customAndViewJSON: any; - recommendModel: IRecommendedModel[]; -} - export type SourceModelTableType = | 'name' | 'id' @@ -25,5 +10,102 @@ export type SourceModelTableType = | 'customAndViewJSON' | 'recommendModel'; -// export type SourceModelDetailTableType = -// | SourceModelTableType +export interface ISourceModelResponse { + createTime: string; + description: string; + id: string; + isCloudModel: boolean; + isInitUserModel: boolean; + isTargetModel: boolean; + onpremModelVersion: string; + onpremiseInfraModel: OnPremiseInfraModel; + updateTime: string; + userId: string; + userModelName: string; + userModelVersion: string; +} + +interface OnPremiseInfraModel { + network: Network; + servers: Server[]; +} + +export interface Network {} + +interface Server { + cpu: CPU; + hostname: string; + interfaces: NetworkInterface[]; + memory: Memory; + os: OperatingSystem; + rootDisk: Disk; + routingTable: RoutingTableEntry[]; +} + +interface CPU { + architecture: string; + cores: number; + cpus: number; + maxSpeed: number; + model: string; + threads: number; + vendor: string; +} + +interface NetworkInterface { + ipv4CidrBlocks?: string[]; + ipv6CidrBlocks?: string[]; + macAddress?: string; + mtu: number; + name: string; + state?: 'up' | 'down'; +} + +interface Memory { + available: number; + totalSize: number; + type: string; + used: number; +} + +interface OperatingSystem { + id: string; + idLike: string; + name: string; + prettyName: string; + version: string; + versionCodename: string; + versionId: string; +} + +interface Disk { + available: number; + label: string; + totalSize: number; + type: string; + used: number; +} + +interface RoutingTableEntry { + destination: string; + gateway?: string; + interface: string; + linkState: 'up' | 'down'; + protocol: string; + scope: string; + source?: string; +} + +export interface IOnpremModelPayload { + onpremiseInfraModel: { + servers: any[]; + network: { + ipv4Networks: any[]; + ipv6Networks: any[]; + }; + }; + description: string; + userModelName: string; + isInitUserModel: boolean; + userModelVersion: string; +} diff --git a/front/src/entities/sourceService/api/index.ts b/front/src/entities/sourceService/api/index.ts index 65d99c18..c7e47e6c 100644 --- a/front/src/entities/sourceService/api/index.ts +++ b/front/src/entities/sourceService/api/index.ts @@ -3,7 +3,10 @@ import { RequestBodyWrapper, useAxiosPost, } from '@/shared/libs'; -import { ISourceAgentAndConnectionStatusResponse } from '@/entities/sourceService/model/types.ts'; +import { + IInfraSourceGroupResponse, + ISourceAgentAndConnectionStatusResponse, +} from '@/entities/sourceService/model/types.ts'; import { axiosInstance } from '@/shared/libs/api/instance.ts'; import type { ISourceGroup } from '@/entities/sourceService/model/types.ts'; @@ -13,6 +16,9 @@ const GET_SOURCE_SERVICE_LIST = 'list-source-group'; const GET_SOURCE_SERVICE = 'get-source-group'; const GET_SOURCE_SERVICE_STATUS = 'agent-and-connection-check'; const DELETE_SOURCE_SERVICE = 'delete-source-group'; +const GET_INFRA_SOURCE_GROUP = 'import-infra-source-group'; +const GET_INFRA_INFO_SOURCE_GROUP_REFINE = + 'get-infra-info-source-group-refined'; export function useRegisterSourceGroup( sourceGroupData: D | ISourceGroup, @@ -86,3 +92,31 @@ export function useBulkDeleteSourceGroup(sourceGroupIds: string[]) { return Promise.all(promiseArr); } + +export function useGetInfraSourceGroup(sourceGroupId: string | null) { + const requestWrapper: Required< + Pick, 'pathParams'> + > = { + pathParams: { sgId: sourceGroupId }, + }; + + return useAxiosPost< + IAxiosResponse, + Required, 'pathParams'>> + >(GET_INFRA_SOURCE_GROUP, requestWrapper); +} + +export function useGetInfraSourceGroupInfraRefine( + sourceGroupId: string | null, +) { + const requestWrapper: Required< + Pick, 'pathParams'> + > = { + pathParams: { sgId: sourceGroupId }, + }; + + return useAxiosPost< + IAxiosResponse, + Required, 'pathParams'>> + >(GET_INFRA_INFO_SOURCE_GROUP_REFINE, requestWrapper); +} diff --git a/front/src/entities/sourceService/model/stores.ts b/front/src/entities/sourceService/model/stores.ts index 3c320798..d7afe187 100644 --- a/front/src/entities/sourceService/model/stores.ts +++ b/front/src/entities/sourceService/model/stores.ts @@ -4,72 +4,60 @@ import { ISourceServiceResponse, SourceServiceStatusType, ISourceServiceResponseElement, + IInfraSourceGroupResponse, } from '@/entities/sourceService/model/types.ts'; import { defineStore } from 'pinia'; const NAMESPACE = 'SOURCESERVICE'; -export interface ISourceServiceStore { - services: Ref; - - serviceWithStatus: any; - - setService(val: any): void; - - getServiceById: (id: string) => ISourceService | null; - - setServiceStatus(serviceId: string, status: any): void; - - setServiceWithConnectionStatus(service: any): void; -} - -export const useSourceServiceStore = defineStore( - NAMESPACE, - (): ISourceServiceStore => { - const services = ref([]); - const serviceWithStatus = ref(); - - function setService(_services: ISourceServiceResponse) { - services.value = _services.source_group.map(service => ({ - id: service.id, - name: service.name, - description: service.description, - connectionCount: - service.connection_info_status_count.connection_info_total, - connectionIds: [], - status: 'S0004', - // status: - })); +export const useSourceServiceStore = defineStore(NAMESPACE, () => { + const services = ref([]); + const serviceWithStatus = ref(); + + function setService(_services: ISourceServiceResponse) { + services.value = _services.source_group.map(service => ({ + id: service.id, + name: service.name, + description: service.description, + connectionCount: + service.connection_info_status_count.connection_info_total, + connectionIds: [], + })); + } + + function getServiceById(serviceId: string) { + return ( + services.value.find((service: ISourceService) => { + return service.id === serviceId; + }) || null + ); + } + + function mappinginfraModel( + sgId: string, + infraSourceGroupResponse: IInfraSourceGroupResponse, + ) { + const sg = getServiceById(sgId); + + if (sg) { + sg.infraModel = infraSourceGroupResponse; } + } - function setServiceWithConnectionStatus( - service: ISourceServiceResponseElement, - ) { - serviceWithStatus.value = service; - } + function mappingSourceGroupStatus(sgId: string, status: string) { + const sg = getServiceById(sgId); - function getServiceById(serviceId: string) { - return ( - services.value.find((service: ISourceService) => { - return service.id === serviceId; - }) || null - ); + if (sg) { + sg.status = status; } - - function setServiceStatus(serviceId: string, status: any) { - const service = getServiceById(serviceId); - if (service) { - service.status = status; - } - } - - return { - services, - serviceWithStatus, - setService, - getServiceById, - setServiceStatus, - setServiceWithConnectionStatus, - }; - }, -); + } + + return { + services, + serviceWithStatus, + setService, + getServiceById, + mappinginfraModel, + mappingSourceGroupStatus, + }; +}); diff --git a/front/src/entities/sourceService/model/types.ts b/front/src/entities/sourceService/model/types.ts index 6d6aad80..3bcfb6a1 100644 --- a/front/src/entities/sourceService/model/types.ts +++ b/front/src/entities/sourceService/model/types.ts @@ -13,7 +13,8 @@ export interface ISourceService { connectionCount: string | number; connectionIds: string[]; // status: SourceServiceStatusType; - status: any; + status?: string; + infraModel?: IInfraSourceGroupResponse; } export interface ISourceAgentAndConnectionStatusResponse { @@ -37,10 +38,10 @@ export interface ISourceConnectionStatusCountResponse { } export const SourceServiceStatus = { - S0001: 'Success', - S0002: 'PartialSuccess', - S0003: 'Failed', - S0004: 'Unknown', + Success: 'Success', + PartialSuccess: 'PartialSuccess', + Failed: 'Failed', + Unknown: 'Unknown', } as const; export type SourceServiceStatusType = keyof typeof SourceServiceStatus; @@ -56,9 +57,19 @@ export interface ISourceServiceResponse { source_group: Array; } +export type IInfraSourceGroupResponse = Array; + +interface IInfraConnectionData { + connection_id: string; + infra_data: string; + saved_time: string; + status: string; +} + export type SourceServiceTableType = | 'name' | 'id' | 'description' | 'connectionCount' - | 'status'; + | 'status' + | 'viewInfra'; diff --git a/front/src/entities/targetModels/api/index.ts b/front/src/entities/targetModels/api/index.ts index e69de29b..8d6eb63a 100644 --- a/front/src/entities/targetModels/api/index.ts +++ b/front/src/entities/targetModels/api/index.ts @@ -0,0 +1,83 @@ +import { + IAxiosResponse, + RequestBodyWrapper, + useAxiosPost, +} from '@/shared/libs'; +import { IRecommendModelResponse } from '@/entities/recommendedModel/model/types.ts'; +import { ITargetModelResponse } from '@/entities'; +import { ISourceConnectionResponse } from '@/entities/sourceConnection/model/types.ts'; + +const CREATE_TARGET_MODEL = 'CreateCloudModel'; +const GET_SOURCE_MODEL_LIST = 'GetUserModel'; +const UPDATE_TARGET_MODEL = 'UpdateCloudmodel'; + +interface ICreateTargetModelPayload { + cloudInfraModel: IRecommendModelResponse['targetInfra']; + csp: string; + description: string; + isInitUserModel: boolean; + isTargetModel: true; + region: string; + userId: string; + userModelName: string; + userModelVersion: string; + zone: string; +} + +export function createTargetModel(data: ICreateTargetModelPayload | null) { + const requestWrapper: Required< + Pick, 'request'> + > = { + request: data, + }; + return useAxiosPost< + IAxiosResponse, + Required< + Pick, 'request'> + > + >(CREATE_TARGET_MODEL, requestWrapper); +} + +export function useGetTargetModelList() { + const requestWrapper: Required, 'pathParams'>> = + { + pathParams: { isTargetModel: 'true' }, + }; + return useAxiosPost< + IAxiosResponse, + Required, 'pathParams'>> + >(GET_SOURCE_MODEL_LIST, requestWrapper); +} + +export function useUpdateTargetModel( + modelId: string | null, + requestData: ICreateTargetModelPayload | null, +) { + const requestBodyWrapper: Pick< + RequestBodyWrapper< + Partial<{ + id: string | null; + requestData: ICreateTargetModelPayload | null; + }> + >, + 'pathParams' | 'request' + > = { + pathParams: { + id: modelId, + }, + request: { requestData }, + }; + + return useAxiosPost< + IAxiosResponse, + Pick< + RequestBodyWrapper< + Partial<{ + id: string | null; + requestData: ICreateTargetModelPayload | null; + }> + >, + 'pathParams' | 'request' + > + >(UPDATE_TARGET_MODEL, requestBodyWrapper); +} diff --git a/front/src/entities/targetModels/model/stores.ts b/front/src/entities/targetModels/model/stores.ts index 50e8b5c9..baa62de1 100644 --- a/front/src/entities/targetModels/model/stores.ts +++ b/front/src/entities/targetModels/model/stores.ts @@ -1,63 +1,24 @@ import { defineStore } from 'pinia'; import { formatDate } from '@/shared/utils'; -import type { ITargetModel } from './types'; -import { dateType } from '@/entities/sourceModels'; +import { ITargetModel, ITargetModelResponse } from './types'; +import { ref } from 'vue'; +import { ISourceModelResponse } from '@/entities'; -export const useTargetModelStore = defineStore('TARGETMODEL', { - state: () => ({ - targetModels: [ - { - id: '10001', - name: 'target model1', - description: 'target model1 description', - migrationType: 'migrationType', - custom: 'Basic', - createdDateTime: dateType, - updatedDateTime: dateType, - modelType: 'Target', - customAndViewJSON: {}, - workflowTool: 'workflowTool', - }, - { - id: '10002', - name: 'target model2', - description: 'target model2 description', - migrationType: 'migrationType', - custom: 'Custom', - createdDateTime: '2021-08-01', - updatedDateTime: '2021-08-01', - modelType: 'Target', - customAndViewJSON: {}, - workflowTool: 'workflowTool', - }, - ] as ITargetModel[], - }), - getters: { - getTargetModelById: state => (id: string) => { - return ( - state.targetModels.find((targetModel: ITargetModel) => { - return targetModel.id === id; - }) || null - ); - }, - }, - actions: { - setTargetModels(_targetModels: ITargetModel[]) { - this.targetModels = _targetModels.map(targetModel => ({ - id: targetModel.id, - name: targetModel.name, - description: targetModel.description, - migrationType: targetModel.migrationType, - custom: targetModel.custom, - createdDateTime: formatDate(targetModel.createdDateTime), - updatedDateTime: formatDate(targetModel.updatedDateTime), - modelType: targetModel.modelType, - customAndViewJSON: targetModel.customAndViewJSON, - workflowTool: targetModel.workflowTool, - })); - }, - setTargetModel(targetModel: ITargetModel) { - this.targetModels = [...this.targetModels, targetModel]; - }, - }, +export const useTargetModelStore = defineStore('TARGETMODEL', () => { + const models = ref([]); + + function getModels() { + return models; + } + function getTargetModelById( + modelId: string, + ): ITargetModelResponse | undefined { + return models.value?.find(el => el.id === modelId); + } + + function setTargetModel(targetModelList: ITargetModelResponse[]) { + models.value = targetModelList; + } + + return { getModels, getTargetModelById, setTargetModel }; }); diff --git a/front/src/entities/targetModels/model/types.ts b/front/src/entities/targetModels/model/types.ts index 3f7ec0ab..e28c1561 100644 --- a/front/src/entities/targetModels/model/types.ts +++ b/front/src/entities/targetModels/model/types.ts @@ -22,3 +22,39 @@ export type TargetModelTableType = | 'modelType' | 'customAndViewJSON' | 'workflowTool'; + +interface Vm { + commonImage: string; + commonSpec: string; + description: string; + label: string | null; + name: string; + subGroupSize: string; +} + +interface CloudInfraModel { + description: string; + installMonAgent: string; + label: string | null; + name: string; + systemLabel: string; + vm: Vm[] | null; +} + +export interface ITargetModelResponse { + cloudInfraModel: CloudInfraModel; + cloudModelVersion: string; + createTime: string; + csp: string; + description: string; + id: string; + isCloudModel: boolean; + isInitUserModel: boolean; + isTargetModel: boolean; + region: string; + updateTime: string; + userId: string; + userModelName: string; + userModelVersion: string; + zone: string; +} diff --git a/front/src/entities/vm/api/api.ts b/front/src/entities/vm/api/api.ts new file mode 100644 index 00000000..a9444235 --- /dev/null +++ b/front/src/entities/vm/api/api.ts @@ -0,0 +1,110 @@ +import { + ILastloadtestStateResponse, + IRunLoadTestRequest, +} from '@/entities/mci/model'; +import { + IAxiosResponse, + RequestBodyWrapper, + useAxiosPost, +} from '@/shared/libs'; +import { IMciRequestParams } from '@/entities/mci/api'; +import { + IGetlastloadtestmetricsResponse, + IGetLoadTestEvaluationDataResponse, + ILoadTestResultAggregateResponse, +} from '@/entities/workspace/model/types.ts'; +const RUN_LOAD_TEST = 'Runloadtest'; +const GET_LAST_LOAD_TEST_CONFIG = 'Getlastloadtestexecutionstate'; +const GET_LOAD_TEST_EVALUATION_DATA = 'Getlastloadtestresult'; +const GET_LOAD_TEST_RESOURCE_METRIC = 'Getlastloadtestmetrics'; + +export function useRunLoadTest(requestPayload: IRunLoadTestRequest | null) { + const requestBodyWrapper: Required< + Pick, 'request'> + > = { + request: requestPayload, + }; + + return useAxiosPost< + IAxiosResponse, + Required, 'request'>> + >(RUN_LOAD_TEST, requestBodyWrapper); +} + +interface ILastloadtestStateResponseWrapper { + result: ILastloadtestStateResponse; +} + +export function useGetLastLoadTestState( + params: IMciRequestParams | { vmId: string } | null, +) { + const requestBodyWrapper: Required< + Pick< + RequestBodyWrapper, + 'request' + > + > = { + request: params, + }; + + return useAxiosPost< + IAxiosResponse, + Required< + Pick< + RequestBodyWrapper, + 'request' + > + > + >(GET_LAST_LOAD_TEST_CONFIG, requestBodyWrapper); +} + +interface IMetricParams extends IMciRequestParams { + vmId: string; + format: 'normal'; +} + +interface IMetricParamsBase extends IMciRequestParams { + vmId: string; +} + +type FormatType = 'normal' | 'aggregate'; + +export function useGetLoadTestEvaluationData( + params: (IMetricParamsBase & { format: T }) | null, +) { + const requestBodyWrapper: Required< + Pick< + RequestBodyWrapper<(IMetricParamsBase & { format: T }) | null>, + 'queryParams' + > + > = { + queryParams: params, + }; + + type ResponseType = T extends 'normal' + ? IGetLoadTestEvaluationDataResponse + : ILoadTestResultAggregateResponse; + + return useAxiosPost< + IAxiosResponse, + Required< + Pick< + RequestBodyWrapper<(IMetricParamsBase & { format: T }) | null>, + 'queryParams' + > + > + >(GET_LOAD_TEST_EVALUATION_DATA, requestBodyWrapper); +} + +export function useGetLoadTestResourceMetric(params: IMetricParams | null) { + const requestBodyWrapper: Required< + Pick, 'queryParams'> + > = { + queryParams: params, + }; + + return useAxiosPost< + IAxiosResponse, + Required, 'queryParams'>> + >(GET_LOAD_TEST_RESOURCE_METRIC, requestBodyWrapper); +} diff --git a/front/src/entities/vm/api/index.ts b/front/src/entities/vm/api/index.ts deleted file mode 100644 index b4f5b5f1..00000000 --- a/front/src/entities/vm/api/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - IAxiosResponse, - RequestBodyWrapper, - useAxiosPost, -} from '../../../shared/libs'; -import { IVm } from '@/entities/mci/model'; - -export interface IVmRequestParams { - nsId: string; - mciId: string; - vmId: string; -} - -const GET_VM_Info = 'GetMciVm'; - -export function useGetVmInfo(params: IVmRequestParams | null) { - const requestBodyWrapper: Required< - Pick, 'pathParams'> - > = { - pathParams: params, - }; - - return useAxiosPost< - IAxiosResponse, - Required, 'pathParams'>> - >(GET_VM_Info, requestBodyWrapper); -} - -export const t = {}; diff --git a/front/src/entities/vm/model/index.ts b/front/src/entities/vm/model/index.ts deleted file mode 100644 index bf0d5c0a..00000000 --- a/front/src/entities/vm/model/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './store'; diff --git a/front/src/entities/vm/model/store.ts b/front/src/entities/vm/model/store.ts deleted file mode 100644 index a211f553..00000000 --- a/front/src/entities/vm/model/store.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { defineStore } from 'pinia'; -import { IVm } from './types.ts'; -import { ref, Ref } from 'vue'; - -const NAMESPACE = 'vm'; - -interface IVmStore { - vms: Ref; - setVm: (val: IVm[]) => void; - addVm: (val: IVm) => void; - loadVmById: (vmId: string) => IVm | null; - loadVmSubGroupById: (vmId: string) => IVm | null; -} - -//TODO ns, mci정보도 추가 저장해야함. -export const useVmStore = defineStore(NAMESPACE, (): IVmStore => { - const vms = ref([]); - - function setVm(_vms: IVm[]) { - vms.value = _vms; - } - - function addVm(_vms: IVm) { - vms.value.push({ ..._vms }); - } - - function loadVmById(vmId: string) { - return ( - vms.value.find(vm => { - return vm.id === vmId; - }) || null - ); - } - - function loadVmSubGroupById(vmSubGrupId: string) { - return ( - vms.value.find(vm => { - return vm.subGroupId === vmSubGrupId; - }) || null - ); - } - - return { vms, setVm, loadVmById, addVm, loadVmSubGroupById }; -}); diff --git a/front/src/entities/vm/model/type.ts b/front/src/entities/vm/model/type.ts new file mode 100644 index 00000000..e69de29b diff --git a/front/src/entities/vm/model/types.ts b/front/src/entities/vm/model/types.ts deleted file mode 100644 index e1aa721f..00000000 --- a/front/src/entities/vm/model/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -export type vmDetailTableType = - | 'serverId' - | 'description' - | 'publicIP' - | 'publicDNS' - | 'privateIP' - | 'privateDNS' - | 'serverStatus' - | 'loadStatus' - | 'provider'; - -export type vmConnectionTableType = - | 'connectionName' - | 'credential' - | 'provider' - | 'driverFileName' - | 'region' - | 'availableZone'; diff --git a/front/src/entities/vmgroups/api/index.ts b/front/src/entities/vmgroups/api/index.ts deleted file mode 100644 index f1be05e0..00000000 --- a/front/src/entities/vmgroups/api/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - IAxiosResponse, - RequestBodyWrapper, - useAxiosPost, -} from '../../../shared/libs'; -import { IVmGroupsResponse } from '../model'; - -export interface IVmGroupRequestParams { - nsId: string; - mciId: string; -} - -const GET_ALL_VM_GROUP = 'GetMciGroupIds'; - -export function useGetVmGroup(props: IVmGroupRequestParams | null) { - const requestBodyWrapper: Required< - Pick, 'pathParams'> - > = { - pathParams: { - nsId: props!.nsId, - mciId: props!.mciId, - }, - }; - - return useAxiosPost< - IAxiosResponse, - Required< - Pick, 'pathParams'> - > - >(GET_ALL_VM_GROUP, requestBodyWrapper); -} diff --git a/front/src/entities/vmgroups/model/index.ts b/front/src/entities/vmgroups/model/index.ts deleted file mode 100644 index e353b144..00000000 --- a/front/src/entities/vmgroups/model/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './store'; -export * from './types'; diff --git a/front/src/entities/vmgroups/model/store.ts b/front/src/entities/vmgroups/model/store.ts deleted file mode 100644 index 89c29df4..00000000 --- a/front/src/entities/vmgroups/model/store.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ref, Ref } from 'vue'; -import { IVmGroup } from './types.ts'; -import { defineStore } from 'pinia'; - -const NAMESPACE = 'vmgroups'; - -export interface IVmGroupsStore { - vmGroups: Ref; - setVmGroups: (val: IVmGroup[]) => void; - loadVmGroupById: (id: string) => IVmGroup | null; -} - -export const useVmListStore = defineStore(NAMESPACE, (): IVmGroupsStore => { - const vmGroups = ref([]); - - function setVmGroups(_vmGroups: IVmGroup[]) { - vmGroups.value = _vmGroups; - } - - function loadVmGroupById(id: string) { - return ( - vmGroups.value.find((vmGroup: IVmGroup): boolean => { - return vmGroup.id === id; - }) || null - ); - } - - return { - setVmGroups, - loadVmGroupById, - vmGroups, - }; -}); diff --git a/front/src/entities/vmgroups/model/types.ts b/front/src/entities/vmgroups/model/types.ts deleted file mode 100644 index e84f4b92..00000000 --- a/front/src/entities/vmgroups/model/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface IVmGroupsResponse { - output: string[]; -} - -export interface IVmGroup { - id: string; -} - -export type ServerGroupTableType = 'id'; diff --git a/front/src/entities/workflow/model/stores.ts b/front/src/entities/workflow/model/stores.ts index 4bf09f34..7b27788c 100644 --- a/front/src/entities/workflow/model/stores.ts +++ b/front/src/entities/workflow/model/stores.ts @@ -62,22 +62,6 @@ export const useWorkflowStore = defineStore(NAMESPACE, () => { })); } - // function getWorkFlowById( - // state: Record, - // workflowId: string, - // ): IWorkflow | null { - // return state[workflowId] || null; - // } - - // function setWorkFlows( - // state: Record, - // res: IWorkflowResponse[], - // ) { - // res.forEach(el => { - // setWorkFlow(state, el); - // }); - // } - function setWorkFlow( state: Record, res: IWorkflowResponse, diff --git a/front/src/entities/workflow/model/types.ts b/front/src/entities/workflow/model/types.ts index 903aaf98..8f514f7d 100644 --- a/front/src/entities/workflow/model/types.ts +++ b/front/src/entities/workflow/model/types.ts @@ -7,6 +7,17 @@ export interface IWorkflow { data: IWorkflowResponse['data']; } +export interface IWorkflowResponse { + data: { + description: string; + task_groups: Array; + }; + name: string; + id: string; + created_at: any; + updated_at: any; +} + export interface ITaskGroupResponse { description: string; name: string; @@ -25,17 +36,6 @@ export interface ITaskResponse { id?: string; } -export interface IWorkflowResponse { - data: { - description: string; - task_groups: Array; - }; - name: string; - id: string; - created_at: any; - updated_at: any; -} - export interface ITaskComponentResponse { created_at: any; data: { diff --git a/front/src/entities/workspace/model/types.ts b/front/src/entities/workspace/model/types.ts index ec7e06f7..27e982fb 100644 --- a/front/src/entities/workspace/model/types.ts +++ b/front/src/entities/workspace/model/types.ts @@ -47,3 +47,72 @@ export interface IWorkspaceRoleResponse { }, ]; } + +interface IResourceMetric { + IsError: boolean; + Timestamp: string; + Unit: string; + Value: string; +} + +export interface IResourceMetricData { + Label: string; + Metrics: IResourceMetric[]; +} + +export interface IGetlastloadtestmetricsResponse { + result: IResourceMetricData[]; +} + +interface IEvaluateMetric { + Bytes: number; + Connection: number; + Elapsed: number; + IdleTime: number; + IsError: boolean; + Latency: number; + No: number; + SentBytes: number; + Timestamp: string; + URL: string; +} + +export interface IEvaluateMetricData { + Label: string; + Results: IEvaluateMetric[]; +} + +export interface IGetLoadTestEvaluationDataResponse { + result: IEvaluateMetricData[]; +} + +export interface ILoadTestResultAggregateResponse { + average: number; + errorPercent: number; + label: string; + maxTime: number; + median: number; + minTime: number; + ninetyFive: number; + ninetyNine: number; + ninetyPercent: number; + receivedKB: number; + requestCount: number; + sentKB: number; + throughput: number; +} + +export type LoadTestResultAggregateTableType = + | 'average' + | 'errorPercent' + | 'label' + | 'maxTime' + | 'median' + | 'minTime' + | 'ninetyFive' + | 'ninetyNine' + | 'ninetyPercent' + | 'receivedKB' + | 'requestCount' + | 'sentKB' + | 'throughput'; diff --git a/front/src/features/auth/model/useAuth.ts b/front/src/features/auth/model/useAuth.ts index 053c80b8..e1807310 100644 --- a/front/src/features/auth/model/useAuth.ts +++ b/front/src/features/auth/model/useAuth.ts @@ -29,6 +29,7 @@ export function useAuth() { expires_in: props.expires_in, }; localStorageConnector.setItem(userData); + console.log(props); authStore.onLogin(props); } diff --git a/front/src/widgets/sourceServices/addSourceServiceModal/index.ts b/front/src/features/sourceServices/addSourceServiceModal/index.ts similarity index 100% rename from front/src/widgets/sourceServices/addSourceServiceModal/index.ts rename to front/src/features/sourceServices/addSourceServiceModal/index.ts diff --git a/front/src/widgets/sourceServices/addSourceServiceModal/ui/AddSourceServiceModal.vue b/front/src/features/sourceServices/addSourceServiceModal/ui/AddSourceServiceModal.vue similarity index 99% rename from front/src/widgets/sourceServices/addSourceServiceModal/ui/AddSourceServiceModal.vue rename to front/src/features/sourceServices/addSourceServiceModal/ui/AddSourceServiceModal.vue index 43ea568d..78481d74 100644 --- a/front/src/widgets/sourceServices/addSourceServiceModal/ui/AddSourceServiceModal.vue +++ b/front/src/features/sourceServices/addSourceServiceModal/ui/AddSourceServiceModal.vue @@ -5,7 +5,7 @@ import { reactive, ref, watchEffect } from 'vue'; import { UpdateSourceService } from '@/features/sourceServices'; import { useRegisterSourceGroup } from '@/entities/sourceService/api'; import { showSuccessMessage, showErrorMessage } from '@/shared/utils'; -import { useSourceConnectionStore } from '@/entities/sourceConnection/model/stores'; +import { useSourceConnectionStore } from '@/entities/sourceConnection/model/stores.ts'; import { useSourceServiceStore } from '@/shared/libs'; import { storeToRefs } from 'pinia'; diff --git a/front/src/widgets/sourceServices/editSourceServiceModal/index.ts b/front/src/features/sourceServices/editSourceServiceModal/index.ts similarity index 100% rename from front/src/widgets/sourceServices/editSourceServiceModal/index.ts rename to front/src/features/sourceServices/editSourceServiceModal/index.ts diff --git a/front/src/widgets/sourceServices/editSourceServiceModal/ui/EditSourceServiceModal.vue b/front/src/features/sourceServices/editSourceServiceModal/ui/EditSourceServiceModal.vue similarity index 96% rename from front/src/widgets/sourceServices/editSourceServiceModal/ui/EditSourceServiceModal.vue rename to front/src/features/sourceServices/editSourceServiceModal/ui/EditSourceServiceModal.vue index 033691f6..e9e9ed7f 100644 --- a/front/src/widgets/sourceServices/editSourceServiceModal/ui/EditSourceServiceModal.vue +++ b/front/src/features/sourceServices/editSourceServiceModal/ui/EditSourceServiceModal.vue @@ -2,9 +2,9 @@ import { i18n } from '@/app/i18n'; import { PButtonModal } from '@cloudforet-test/mirinae'; import { reactive, ref, watch, watchEffect } from 'vue'; -import EditSourceService from '@/features/sourceServices/updateSourceService/ui/EditSourceService.vue'; +import EditSourceService from '../../updateSourceService/ui/EditSourceService.vue'; import { useSourceServiceDetailModel } from '@/widgets/source/sourceServices/sourceServiceDetail/model/sourceServiceDetailModel.ts'; -import { ISourceService } from '@/entities/sourceService/model/types'; +import { ISourceService } from '@/entities/sourceService/model/types.ts'; import { useUpdateSourceGroup } from '@/entities/sourceService/api'; import { showErrorMessage } from '@/shared/utils'; diff --git a/front/src/widgets/sourceServices/jsonViewer/ui/JsonViewer.vue b/front/src/features/sourceServices/jsonViewer/ui/JsonViewer.vue similarity index 67% rename from front/src/widgets/sourceServices/jsonViewer/ui/JsonViewer.vue rename to front/src/features/sourceServices/jsonViewer/ui/JsonViewer.vue index 63e05704..f27dec2d 100644 --- a/front/src/widgets/sourceServices/jsonViewer/ui/JsonViewer.vue +++ b/front/src/features/sourceServices/jsonViewer/ui/JsonViewer.vue @@ -3,50 +3,26 @@ import { i18n } from '@/app/i18n'; import { collectJsonEditor } from '@/features/sourceServices'; import { PI, PSpinner } from '@cloudforet-test/mirinae'; import { ref, watch } from 'vue'; +import { AxiosResponse } from 'axios'; +import { IUseAxiosWrapperReturnType } from '@/shared/libs'; +import { useGetInfraInfoRefined } from '@/entities/sourceConnection/api'; +import { showErrorMessage } from '@/shared/utils'; interface iProps { formData: string | undefined; - schema: { - json: boolean; - properties: object; - }; + promiseFunc: (payload?: any, config?: any) => Promise>; + convertedJSON: string | undefined; + loading: boolean; } const props = defineProps(); const emit = defineEmits(['update:is-converted']); -const convertedJson = ref(''); -const isConverted = ref(false); - -const handleConvertJson = () => { - // TODO: convert button action 미정 - convertedJson.value = props.formData; - emit('update:is-converted'); -}; - -watch( - convertedJson, - nv => { - if (nv && nv.length > 0) { - isConverted.value = true; - } else { - isConverted.value = false; - } - }, - { immediate: true }, -); - -watch( - isConverted, - nv => { - if (nv) { - setTimeout(() => { - isConverted.value = false; - }, 1000); - } - }, - { immediate: true }, -); +function handleConvertJson() { + props.promiseFunc().then(res => { + emit('update:is-converted', res); + }); +} @@ -82,31 +56,38 @@ watch( diff --git a/front/src/features/workload/successfullyModal/ui/SuccessfullyLoadConfigModal.vue b/front/src/features/workload/successfullyModal/ui/SuccessfullyLoadConfigModal.vue new file mode 100644 index 00000000..dea2e39f --- /dev/null +++ b/front/src/features/workload/successfullyModal/ui/SuccessfullyLoadConfigModal.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/front/src/main.ts b/front/src/main.ts index 1aef22a4..7ad3f269 100644 --- a/front/src/main.ts +++ b/front/src/main.ts @@ -3,11 +3,11 @@ import MirinaeDesignSystem from '@cloudforet-test/mirinae'; import '@cloudforet-test/mirinae/dist/style.css'; // import '@cloudforet-test/mirinae/css/light-style.css'; import { App } from './app'; -import './app/style/style.pcss'; import { createPinia, PiniaVuePlugin } from 'pinia'; import VueRouter from 'vue-router'; import { McmpRouter } from './app/providers/router'; import { i18n } from './app/i18n'; +import './app/style/style.pcss'; const pinia = createPinia(); Vue.use(PiniaVuePlugin); diff --git a/front/src/pages/main/MainPage.vue b/front/src/pages/main/MainPage.vue index 4ac3020f..088e1aae 100644 --- a/front/src/pages/main/MainPage.vue +++ b/front/src/pages/main/MainPage.vue @@ -1,12 +1,12 @@ diff --git a/front/src/pages/models/index.ts b/front/src/pages/models/index.ts index 8790fea6..4cdf41d6 100644 --- a/front/src/pages/models/index.ts +++ b/front/src/pages/models/index.ts @@ -1,3 +1,2 @@ export * from './sourceModels'; export * from './targetModels'; -export * from './recommendModelList'; diff --git a/front/src/pages/models/recommendModelList/index.ts b/front/src/pages/models/recommendModelList/index.ts deleted file mode 100644 index 32c71660..00000000 --- a/front/src/pages/models/recommendModelList/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import RecommendedModelList from './ui/RecommendedModelList.vue'; - -export { RecommendedModelList }; diff --git a/front/src/pages/models/recommendModelList/ui/RecommendedModelList.vue b/front/src/pages/models/recommendModelList/ui/RecommendedModelList.vue deleted file mode 100644 index eb81a78d..00000000 --- a/front/src/pages/models/recommendModelList/ui/RecommendedModelList.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/front/src/pages/models/sourceModels/ui/SourceModelsPage.vue b/front/src/pages/models/sourceModels/ui/SourceModelsPage.vue index 885b9490..db07967d 100644 --- a/front/src/pages/models/sourceModels/ui/SourceModelsPage.vue +++ b/front/src/pages/models/sourceModels/ui/SourceModelsPage.vue @@ -4,15 +4,28 @@ import { SourceModelList } from '@/widgets/models/sourceModels'; import { SourceModelDetail } from '@/widgets/models/sourceModels'; import { CustomViewSourceModel } from '@/widgets/models/sourceModels'; import { RecommendedModel } from '@/widgets/models/sourceModels'; -import { reactive, ref } from 'vue'; +import { computed, reactive, ref, watch } from 'vue'; import { SimpleEditForm } from '@/widgets/layout'; +import { showErrorMessage, showSuccessMessage } from '@/shared/utils'; +import { + useBulkAddWorkspaceList, + useSourceModelStore, + useUpdateSourceModel, +} from '@/entities'; const pageName = 'Source Models'; const selectedSourceModelId = ref(''); const sourceModelName = ref(''); const sourceModelDescription = ref(''); - +const sourceModelStore = useSourceModelStore(); +const resUpdateSourceModel = useUpdateSourceModel(null, null); +const sourceModel = computed(() => + sourceModelStore.getSourceModelById(selectedSourceModelId.value), +); +watch(sourceModelName, nv => { + console.log(nv); +}); const mainTabState = reactive({ activeTab: 'details', tabs: [ @@ -24,20 +37,56 @@ const mainTabState = reactive({ }); const modalState = reactive({ - customViewJsonModal: { open: false, trigger: false }, - editModelModal: { open: false, trigger: false }, + customViewJsonModal: { + open: false, + trigger: false, + }, + editModelModal: { + open: false, + trigger: false, + context: { + name: '', + description: '', + }, + updateTrigger() { + modalState.editModelModal.trigger = false; + }, + }, viewRecommendedListModal: { open: false, trigger: false }, }); -function handleClickSourceModelId(id: string) { - selectedSourceModelId.value = id; +function handleClickSourceModelId(data: { id: string; name: string }) { + console.log(data); + selectedSourceModelId.value = data.id; + sourceModelName.value = data.name ?? ''; } function handleJsonModal(value: boolean) { modalState.customViewJsonModal.open = value; } -const recommendedModelList = ref([]); +function handleUpdateSourceModel(e) { + modalState.editModelModal.open = false; + modalState.editModelModal.context.name = e.name; + modalState.editModelModal.context.description = e.description; + console.log(sourceModel.value); + const requestBody = Object.assign({}, sourceModel.value, { + userModelName: e.name, + description: e.description, + }); + resUpdateSourceModel + .execute({ + pathParams: { id: selectedSourceModelId.value }, + request: requestBody, + }) + .then(res => { + showSuccessMessage('success', 'Successfully updated target model'); + modalState.editModelModal.trigger = true; + }) + .catch(e => { + showErrorMessage('error', e.errorMsg); + }); +} @@ -87,22 +135,25 @@ const recommendedModelList = ref([]); header-title="Edit Model" name-label="Model Name" :name="sourceModelName" - :description="sourceModelDescription" + :description="sourceModel['description'] ?? ''" :name-placeholder="'Model Name'" - @update:save-modal="modalState.editModelModal.open = false" + @update:save-modal="handleUpdateSourceModel" @update:close-modal="modalState.editModelModal.open = false" + @update:trigger="modalState.editModelModal.trigger = true" />
(''); +const selectedTargetModelName = ref(''); const targetModelName = ref(''); const targetModelDescription = ref(''); +const resUpdateTargetModel = useUpdateTargetModel(null, null); +const targetModelStore = useTargetModelStore(); const mainTabState = reactive({ activeTab: 'details', @@ -23,14 +32,57 @@ const mainTabState = reactive({ ], }); -const modalState = reactive({ - editModelModal: { open: false, trigger: false }, - customViewJsonModal: { open: false, trigger: false }, - workflowEditorModal: { open: false, trigger: false }, +const modalStates = reactive({ + editModelModal: { + open: false, + context: { + name: '', + description: '', + }, + trigger: false, + updateTrigger() { + modalStates.editModelModal.trigger = false; + }, + }, + customViewJsonModal: { + open: false, + }, + workflowEditorModal: { + open: false, + }, }); -function handleClickTargetModelId(id: string) { - selectedTargetModelId.value = id; +function handleClickTargetModel(data: { id: string; name: string }) { + selectedTargetModelId.value = data.id; + selectedTargetModelName.value = data.name; +} + +function handleUpdateTargetModel(e) { + const targetModel = targetModelStore.getTargetModelById( + selectedTargetModelId.value, + ); + + modalStates.editModelModal.open = false; + modalStates.editModelModal.context.name = e.name; + modalStates.editModelModal.context.description = e.description; + + const requestBody = Object.assign({}, targetModel, { + userModelName: e.name, + description: e.description, + }); + + resUpdateTargetModel + .execute({ + pathParams: { id: selectedTargetModelId.value }, + request: requestBody, + }) + .then(res => { + showSuccessMessage('success', 'Successfully updated target model'); + modalStates.editModelModal.trigger = true; + }) + .catch(e => { + showErrorMessage('error', e.errorMsg); + }); } @@ -40,7 +92,11 @@ function handleClickTargetModelId(id: string) {

{{ pageName }}

- +
@@ -76,30 +132,35 @@ function handleClickTargetModelId(id: string) {
diff --git a/front/src/pages/sourceServices/ui/SourceServicePage.vue b/front/src/pages/sourceServices/ui/SourceServicePage.vue index 4e84d01a..2f8d7757 100644 --- a/front/src/pages/sourceServices/ui/SourceServicePage.vue +++ b/front/src/pages/sourceServices/ui/SourceServicePage.vue @@ -8,16 +8,13 @@ import SourceInformation from '@/widgets/source/sourceConnections/sourceConnecti import SourceInfraCollect from '@/widgets/source/sourceConnections/sourceConnectionDetail/infraCollect/ui/SourceInfraCollect.vue'; import SourceSoftwareCollect from '@/widgets/source/sourceConnections/sourceConnectionDetail/softwareCollect/ui/SourceSoftwareCollect.vue'; import SourceConnectionModal from '@/widgets/source/sourceConnections/sourceConnectionModal/ui/SourceConnectionModal.vue'; -import { - AddSourceServiceModal, - EditSourceServiceModal, -} from '@/widgets/sourceServices'; import { useSidebar } from '@/shared/libs/store/sidebar'; import { storeToRefs } from 'pinia'; import MetaViewer from '@/widgets/source/sourceConnections/sourceConnectionDetail/metaViewer/ui/MetaViewer.vue'; import { useSourceInfraCollectModel } from '@/widgets/source/sourceConnections/sourceConnectionDetail/infraCollect/model/sourceInfraCollectModel.ts'; import EditSourceConnectionModal from '@/widgets/source/sourceConnections/sourceConnectionModal/ui/EditSourceConnectionModal.vue'; import { showSuccessMessage } from '@/shared/utils'; +import { useGetInfraInfoRefined } from '@/entities/sourceConnection/api'; const sourceConnectionName = ref(''); const multiSelectedConnectionIds = ref([]); @@ -146,46 +143,6 @@ const data = computed(() => { return sourceConnectionStore.getConnectionById(selectedConnectionId.value) ?.softwareData; }); - -const infraSchema = { - json: true, - properties: { - compute: { - type: 'string', - title: 'Compute', - }, - network: { - type: 'string', - title: 'Network', - }, - storage: { - type: 'string', - title: 'Storage', - }, - }, -}; - -const softwareSchema = { - json: true, - properties: { - deb: { - type: 'string', - title: 'Debian', - }, - docker: { - type: 'string', - title: 'Docker', - }, - podman: { - type: 'string', - title: 'Podman', - }, - rpm: { - type: 'string', - title: 'RPM', - }, - }, -}; diff --git a/front/src/shared/hooks/vm/index.ts b/front/src/shared/hooks/vm/index.ts index f48bdbac..692fa3d5 100644 --- a/front/src/shared/hooks/vm/index.ts +++ b/front/src/shared/hooks/vm/index.ts @@ -11,19 +11,21 @@ export function getCloudProvidersInVms(vms: IVm[]) { } }); - return provider; + return Object.values(provider); } function providerColor(provider: string) { + const upperCaseProvider = provider.toUpperCase(); + let color = '#EDEDEF'; - switch (provider) { + switch (upperCaseProvider) { case 'AWS': color = '#FF9900'; break; - case 'Google': + case 'GOOGLE': color = '#4387F4'; break; - case 'Azure': + case 'AZURE': color = '#00BCF0'; break; case 'NHN': @@ -31,5 +33,8 @@ function providerColor(provider: string) { break; } - return color; + return { + name: provider, + color: color, + }; } diff --git a/front/src/shared/libs/api/bulkRequest.ts b/front/src/shared/libs/api/bulkRequest.ts index 657a36ac..fef64a41 100644 --- a/front/src/shared/libs/api/bulkRequest.ts +++ b/front/src/shared/libs/api/bulkRequest.ts @@ -4,8 +4,8 @@ import { axiosInstance } from './instance.ts'; import { AsyncStatus, extractErrorMessage, - IUseAxiosErrorDetail, IUseBulkAxiosWrapperReturnType, + IUseAxiosErrorDetail, } from '../index'; export function axiosBulkPost = any>( @@ -26,7 +26,7 @@ function useAxiosWrapper = any>( ) => Promise[]>, ): IUseBulkAxiosWrapperReturnType | IUseAxiosErrorDetail { const isLoading: Ref = ref(false); - const data: Ref = ref(null); // T[]로 정의 + const data: Ref = ref(null); const error: Ref = ref(null); const errorMsg: Ref = ref(null); const status: Ref = ref('idle'); @@ -35,14 +35,13 @@ function useAxiosWrapper = any>( payload?: D, config?: AxiosRequestConfig, ): Promise[]> => { - // T[]로 수정 isLoading.value = true; status.value = 'loading'; let result; try { result = await apiCall(payload, config); reset(); - data.value = result.map(res => res.data); // T[]로 저장 + data.value = result.map(res => res.data); status.value = 'success'; return Promise.resolve(result); } catch (e: any) { diff --git a/front/src/shared/libs/api/request.ts b/front/src/shared/libs/api/request.ts index 01c83dcd..1c3a3c04 100644 --- a/front/src/shared/libs/api/request.ts +++ b/front/src/shared/libs/api/request.ts @@ -98,6 +98,9 @@ export function extractErrorMessage(error: any): string | null { if (errorData.error) { return errorData.error; } + if (errorData.responseData?.errorMessage) { + return errorData.responseData.errorMessage; + } return errorData.message || error.message || 'Unknown error occurred'; } else if (error.request) { // 요청은 되었으나 서버로부터 응답이 없음 diff --git a/front/src/shared/utils/dateformatter/index.ts b/front/src/shared/utils/dateformatter/index.ts index aad44af1..b9d01495 100644 --- a/front/src/shared/utils/dateformatter/index.ts +++ b/front/src/shared/utils/dateformatter/index.ts @@ -2,16 +2,49 @@ export function formatDate( date: Date | string, format: string = 'yyyy-MM-dd HH:mm:ss', ): string { - const d = new Date(date); + try { + const d = new Date(date); + if (isNaN(d.getTime())) { + throw new Error('Invalid date'); + } - const map: { [key: string]: string } = { - yyyy: d.getFullYear().toString(), - MM: String(d.getMonth() + 1).padStart(2, '0'), - dd: String(d.getDate()).padStart(2, '0'), - HH: String(d.getHours()).padStart(2, '0'), - mm: String(d.getMinutes()).padStart(2, '0'), - ss: String(d.getSeconds()).padStart(2, '0'), - }; + const map: { [key: string]: string } = { + yyyy: d.getFullYear().toString(), + MM: String(d.getMonth() + 1).padStart(2, '0'), + dd: String(d.getDate()).padStart(2, '0'), + HH: String(d.getHours()).padStart(2, '0'), + mm: String(d.getMinutes()).padStart(2, '0'), + ss: String(d.getSeconds()).padStart(2, '0'), + }; - return format.replace(/yyyy|MM|dd|HH|mm|ss/g, matched => map[matched]); + return format.replace(/yyyy|MM|dd|HH|mm|ss/g, matched => map[matched]); + } catch (e) { + return ''; + } +} + +export function formatDateWithMilliseconds( + date: Date | string, + format: string = 'yyyy-MM-dd HH:mm:ss.SSS', +): string { + try { + const d = new Date(date); + if (isNaN(d.getTime())) { + throw new Error('Invalid date'); + } + + const map: { [key: string]: string } = { + yyyy: d.getFullYear().toString(), + MM: String(d.getMonth() + 1).padStart(2, '0'), + dd: String(d.getDate()).padStart(2, '0'), + HH: String(d.getHours()).padStart(2, '0'), + mm: String(d.getMinutes()).padStart(2, '0'), + ss: String(d.getSeconds()).padStart(2, '0'), + SSS: String(d.getMilliseconds()).padStart(3, '0'), // 밀리세컨드 추가 + }; + + return format.replace(/yyyy|MM|dd|HH|mm|ss|SSS/g, matched => map[matched]); + } catch (e) { + return ''; + } } diff --git a/front/src/widgets/layout/simpleEditForm/ui/SimpleEditForm.vue b/front/src/widgets/layout/simpleEditForm/ui/SimpleEditForm.vue index e79cd595..6a8dabfd 100644 --- a/front/src/widgets/layout/simpleEditForm/ui/SimpleEditForm.vue +++ b/front/src/widgets/layout/simpleEditForm/ui/SimpleEditForm.vue @@ -22,8 +22,7 @@ const props = defineProps(); const emit = defineEmits([ 'update:save-modal', 'update:close-modal', - 'update:name-value', - 'update:description', + 'update:trigger', ]); const _name = ref(props.name); @@ -41,17 +40,14 @@ watchEffect( ); function handleConfirm() { - emit('update:save-modal'); - emit('update:description', _description.value); + emit('update:save-modal', { + name: _name.value, + description: _description.value, + }); + emit('update:trigger'); } -watch( - _name, - () => { - emit('update:name-value', _name.value); - }, - { immediate: true }, -); +watch(_name, () => {}, { immediate: true });