├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── conf ├── DBTopMonitoringSolution.template ├── api.core.service ├── server.conf ├── setup.sh └── update.sh ├── docs └── aurora.limitless.md ├── frontend ├── package.json ├── public │ ├── index.html │ ├── manifest.json │ ├── robots.txt │ └── version.json ├── src │ ├── components │ │ ├── Animation01.js │ │ ├── ChartArea01.js │ │ ├── ChartArea02.js │ │ ├── ChartArea03.js │ │ ├── ChartBar01.js │ │ ├── ChartBar02.js │ │ ├── ChartBar03.js │ │ ├── ChartBar04.js │ │ ├── ChartBar05.js │ │ ├── ChartCLW01.js │ │ ├── ChartCLW02.js │ │ ├── ChartCLW03.js │ │ ├── ChartColumn01.js │ │ ├── ChartColumn02.js │ │ ├── ChartLine02.js │ │ ├── ChartLine03.js │ │ ├── ChartLine04.js │ │ ├── ChartLine05.js │ │ ├── ChartPie-01.js │ │ ├── ChartPolar-01.js │ │ ├── ChartPolar-02.js │ │ ├── ChartProgressBar-01.js │ │ ├── ChartRadialBar01.js │ │ ├── ChartSparkline01.js │ │ ├── CompAuroraLimitlessNode01.js │ │ ├── CompElasticNode01.js │ │ ├── Functions.js │ │ ├── Header.js │ │ ├── HeaderApp.js │ │ ├── Layout.js │ │ ├── Metric01.js │ │ ├── Metric02.js │ │ ├── Metric03.js │ │ ├── Metric04.js │ │ ├── ProtectedApp.js │ │ ├── ProtectedDb.js │ │ ├── Table01.js │ │ └── Table02.js │ ├── index.css │ ├── index.js │ ├── pages │ │ ├── Authentication.js │ │ ├── Configs.js │ │ ├── Home.js │ │ ├── Logout.js │ │ ├── Sm-appUpdate.js │ │ ├── Sm-aurora-mysql-01.js │ │ ├── Sm-aurora-postgresql-01.js │ │ ├── Sm-aurora-postgresql-02.js │ │ ├── Sm-clustersAurora-01.js │ │ ├── Sm-clustersDocumentDB-01.js │ │ ├── Sm-clustersElasticache-01.js │ │ ├── Sm-clustersMemorydb-01.js │ │ ├── Sm-documentdb-01.js │ │ ├── Sm-documentdb-02.js │ │ ├── Sm-dynamodb-01.js │ │ ├── Sm-elasticache-01.js │ │ ├── Sm-elasticache-02.js │ │ ├── Sm-engineConnections.js │ │ ├── Sm-memorydb-01.js │ │ ├── Sm-mssql-01.js │ │ ├── Sm-mysql-01.js │ │ ├── Sm-oracle-01.js │ │ ├── Sm-postgresql-01.js │ │ ├── Sm-rdsInstances-01.js │ │ └── Sm-tablesDynamoDB-01.js │ └── styles │ │ └── css │ │ ├── animation.css │ │ ├── base.css │ │ ├── default.css │ │ └── top-navigation.css └── www │ ├── index.html │ └── loading.gif ├── images ├── dbtop.aurora.limitless.gif ├── dbtop.aurora.limitless.png ├── image01.png ├── image02.png ├── image03.png ├── image04.png ├── image05.png ├── image06.png └── vid02.mp4 └── server ├── api.core.js ├── aws-node-types.json ├── class.aws.js ├── class.engine.js ├── class.logging.js ├── class.update.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | frontend/public/aws-exports.json 2 | server/aws-exports.json 3 | 4 | # General 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | # Logs 33 | logs 34 | *.log 35 | npm-debug.log* 36 | yarn-debug.log* 37 | yarn-error.log* 38 | lerna-debug.log* 39 | .pnpm-debug.log* 40 | 41 | # Diagnostic reports (https://nodejs.org/api/report.html) 42 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 43 | 44 | # Runtime data 45 | pids 46 | *.pid 47 | *.seed 48 | *.pid.lock 49 | 50 | # Directory for instrumented libs generated by jscoverage/JSCover 51 | lib-cov 52 | 53 | # Coverage directory used by tools like istanbul 54 | coverage 55 | *.lcov 56 | 57 | # nyc test coverage 58 | .nyc_output 59 | 60 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 61 | .grunt 62 | 63 | # Bower dependency directory (https://bower.io/) 64 | bower_components 65 | 66 | # node-waf configuration 67 | .lock-wscript 68 | 69 | # Compiled binary addons (https://nodejs.org/api/addons.html) 70 | build/Release 71 | 72 | # Dependency directories 73 | node_modules/ 74 | jspm_packages/ 75 | 76 | # Snowpack dependency directory (https://snowpack.dev/) 77 | web_modules/ 78 | 79 | # TypeScript cache 80 | *.tsbuildinfo 81 | 82 | # Optional npm cache directory 83 | .npm 84 | 85 | # Optional eslint cache 86 | .eslintcache 87 | 88 | # Optional stylelint cache 89 | .stylelintcache 90 | 91 | # Microbundle cache 92 | .rpt2_cache/ 93 | .rts2_cache_cjs/ 94 | .rts2_cache_es/ 95 | .rts2_cache_umd/ 96 | 97 | # Optional REPL history 98 | .node_repl_history 99 | 100 | # Output of 'npm pack' 101 | *.tgz 102 | 103 | # Yarn Integrity file 104 | .yarn-integrity 105 | 106 | # dotenv environment variable files 107 | .env 108 | .env.development.local 109 | .env.test.local 110 | .env.production.local 111 | .env.local 112 | 113 | # parcel-bundler cache (https://parceljs.org/) 114 | .cache 115 | .parcel-cache 116 | 117 | # Next.js build output 118 | .next 119 | out 120 | 121 | # Nuxt.js build / generate output 122 | .nuxt 123 | dist 124 | 125 | # Gatsby files 126 | .cache/ 127 | # Comment in the public line in if your project uses Gatsby and not Next.js 128 | # https://nextjs.org/blog/next-9-1#public-directory-support 129 | # public 130 | 131 | # vuepress build output 132 | .vuepress/dist 133 | 134 | # vuepress v2.x temp and cache directory 135 | .temp 136 | .cache 137 | 138 | # Docusaurus cache and generated files 139 | .docusaurus 140 | 141 | # Serverless directories 142 | .serverless/ 143 | 144 | # FuseBox cache 145 | .fusebox/ 146 | 147 | # DynamoDB Local files 148 | .dynamodb/ 149 | 150 | # TernJS port file 151 | .tern-port 152 | 153 | # Stores VSCode versions used for testing VSCode extensions 154 | .vscode-test 155 | 156 | # yarn v2 157 | .yarn/cache 158 | .yarn/unplugged 159 | .yarn/build-state.yml 160 | .yarn/install-state.gz 161 | .pnp.* 162 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 4 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 5 | opensource-codeofconduct@amazon.com with any additional questions or comments. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | ## Reporting Bugs/Feature Requests 10 | 11 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 12 | 13 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 14 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 15 | 16 | * A reproducible test case or series of steps 17 | * The version of our code being used 18 | * Any modifications you've made relevant to the bug 19 | * Anything unusual about your environment or deployment 20 | 21 | ## Contributing via Pull Requests 22 | 23 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 24 | 25 | 1. You are working against the latest source on the *main* branch. 26 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 27 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 28 | 29 | To send us a pull request, please: 30 | 31 | 1. Fork the repository. 32 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 33 | 3. Ensure local tests pass. 34 | 4. Commit to your fork using clear commit messages. 35 | 5. Send us a pull request, answering any default questions in the pull request interface. 36 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 37 | 38 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 39 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 40 | 41 | ## Finding contributions to work on 42 | 43 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 44 | 45 | ## Code of Conduct 46 | 47 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 48 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 49 | opensource-codeofconduct@amazon.com with any additional questions or comments. 50 | 51 | ## Security issue notifications 52 | 53 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 54 | 55 | ## Licensing 56 | 57 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | DBTop Monitoring 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | ********************** 5 | THIRD PARTY COMPONENTS 6 | ********************** 7 | This software includes third party software subject to the following copyrights: 8 | 9 | aws-amplify/ui-react under the Apache License 2.0 10 | cloudscape-design/components under the Apache License 2.0 11 | cloudscape-design/design-tokens under the Apache License 2.0 12 | cloudscape-design/global-styles under the Apache License 2.0 13 | awsui/components-react under the Apache License 2.0 14 | testing-library/jest-dom under the Massachusetts Institute of Technology (MIT) license 15 | testing-library/react under the Massachusetts Institute of Technology (MIT) license 16 | testing-library/user-event under the Massachusetts Institute of Technology (MIT) license 17 | axios under the Massachusetts Institute of Technology (MIT) license 18 | crypto-js under the Massachusetts Institute of Technology (MIT) license 19 | react under the Massachusetts Institute of Technology (MIT) license 20 | react-apexcharts under the Massachusetts Institute of Technology (MIT) license 21 | react-dom under the Massachusetts Institute of Technology (MIT) license 22 | react-router-dom under the Massachusetts Institute of Technology (MIT) license 23 | react-scripts under the Massachusetts Institute of Technology (MIT) license 24 | web-vitals under the Apache License 2.0 25 | aws-sdk under the Apache License 2.0 26 | request under the Apache License 2.0 27 | cors under the Massachusetts Institute of Technology (MIT) license 28 | express under the Massachusetts Institute of Technology (MIT) license 29 | jsonwebtoken under the Massachusetts Institute of Technology (MIT) license 30 | jwk-to-pem under the Apache License 2.0 31 | mysql2 under the Massachusetts Institute of Technology (MIT) license 32 | mssql under the Massachusetts Institute of Technology (MIT) license 33 | oracledb under the Apache License 2.0 34 | pg under the Massachusetts Institute of Technology (MIT) license 35 | redis under the Massachusetts Institute of Technology (MIT) license 36 | redis-info under the Massachusetts Institute of Technology (MIT) license 37 | uuid under the Massachusetts Institute of Technology (MIT) license 38 | 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DBTop Monitoring Solution for AWS Database Services 2 | 3 | > **Disclaimer:** The sample code; software libraries; command line tools; proofs of concept; templates; or other related technology (including any of the foregoing that are provided by our personnel) is provided to you as AWS Content under the AWS Customer Agreement, or the relevant written agreement between you and AWS (whichever applies). You are responsible for testing, securing, and optimizing the AWS Content, such as sample code, as appropriate for production grade use based on your specific quality control practices and standards. Deploying AWS Content may incur AWS charges for creating or using AWS chargeable resources, such as running Amazon EC2 instances, using Amazon CloudWatch or Amazon Cognito. 4 | 5 | 6 | 7 | ## What is DBTop Monitoring ? 8 | 9 | DBTop Monitoring is evolution of [RDSTop Monitoring Solution](https://github.com/aws-samples/rds-top-monitoring) initiative. 10 | 11 | DBTop Monitoring is lightweight application to perform real-time monitoring for AWS Database Resources. 12 | Based on same simplicity concept of Unix top utility, provide quick and fast view of database performance, just all in one screen. 13 | 14 | image 15 | 16 | 17 | 18 | ## How does DBTop Monitoring look like? 19 | 20 | image 21 | 22 | image 23 | 24 | image 25 | 26 | 27 | ### [Visit the YouTube channel for videos](https://www.youtube.com/@DBTopMonitoringSolution) 28 | 29 | 30 | 31 | ## How it works? 32 | 33 | image 34 | 35 | 36 | 37 | ## Database engine support 38 | 39 | DBTop Monitoring Solution currently supports following database engines: 40 | 41 | - [Amazon Relational Database Service (RDS)](https://aws.amazon.com/rds/) 42 | - [Amazon RDS for MySQL](https://aws.amazon.com/rds/mysql/) 43 | - [Amazon RDS for PostgreSQL](https://aws.amazon.com/rds/postgresql/) 44 | - [Amazon RDS for MariaDB](https://aws.amazon.com/rds/mariadb/) 45 | - [Amazon RDS for Oracle](https://aws.amazon.com/rds/oracle/) 46 | - [Amazon RDS for SQLServer](https://aws.amazon.com/rds/sqlserver/) 47 | - [Amazon Aurora Instance (MySQL-Compatible Edition)](https://aws.amazon.com/rds/aurora/) 48 | - [Amazon Aurora Instance (MySQL-Compatible Edition)](https://aws.amazon.com/rds/aurora/) 49 | - [Amazon Aurora Instance (PostgreSQL-Compatible Edition)](https://aws.amazon.com/rds/aurora/) 50 | - [Amazon Aurora Clusters (MySQL-Compatible Edition)](https://aws.amazon.com/rds/aurora/) 51 | - [Amazon Aurora Clusters (PostgreSQL-Compatible Edition)](https://aws.amazon.com/rds/aurora/) 52 | - [Amazon In-Memory Databases](https://aws.amazon.com/elasticache/) 53 | - [Amazon ElastiCache for Redis](https://aws.amazon.com/elasticache/redis/) 54 | - [Amazon MemoryDB for Redis](https://aws.amazon.com/memorydb/) 55 | - [Amazon DocumentDB (with MongoDB compatibility)](https://aws.amazon.com/documentdb/) 56 | 57 | Additional expanded support coming later to : 58 | 59 | - [Amazon OpenSearch Service](https://aws.amazon.com/opensearch-service/) 60 | 61 | 62 | 63 | ## Solution Components 64 | 65 | - **Frontend.** React Developed Application to provide user interface to visualize performance database information. 66 | 67 | - **Backend.** NodeJS API Component to gather performance information from database engines, AWS CloudWatch and Enhanced Monitoring. 68 | 69 | 70 | 71 | ## Architecture 72 | 73 | image 74 | 75 | 76 | 77 | ## Use cases 78 | 79 | - **Monitor instance performance.** 80 | Visualize performance data on realtime, and correlate data to understand and resolve the root cause of performance issues in your database instances. 81 | 82 | - **Perform root cause analysis.** 83 | Analyze database and operating system metrics to speed up debugging and reduce overall mean time to resolution. 84 | 85 | - **Optimize resources proactively.** 86 | Identify top consumer sessions, gather SQL statements and resource usages. 87 | 88 | 89 | 90 | ## Solution Requirements 91 | 92 | #### [Amazon RDS](https://aws.amazon.com/rds/) Enhanced Monitoring 93 | 94 | [Amazon RDS](https://aws.amazon.com/rds/) provides metrics in real time for the operating system (OS) that your DB instance runs on. DBTop Monitoring solution integrate metrics from Enhanced Monitoring and it has to be enabled. Follow procedure below to turn on Enhanced Monitoring. 95 | 96 | [Setting up and enabling Enhanced Monitoring](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.OS.Enabling.html) 97 | 98 | 99 | #### VPC Network Access to AWS Database Instances 100 | 101 | DBTop Monitoring Solution needs to access privately AWS Database resources, grant inboud access for security groups used by database resources. 102 | 103 | _First, you need to take a note of the Cloudformation resources created by the stack_ 104 | 105 | Cloudformation Security Group Id 106 | 107 | 108 | _Second, you need to edit the Security Group that allows access to your Database resources, in this case to ElastiCache_ 109 | 110 | Edit Security Group Id to Allow Redis 111 | 112 | 113 | ## Resource Usage and Cost 114 | 115 | DBTop Monitoring Solution will use following resources: 116 | 117 | - **AWS EC2 instance.** 118 | The cost of this resource will depend of size selected during the deployment process. AWS EC2 instance pricing can be review [here](https://aws.amazon.com/ec2/pricing/). 119 | 120 | - **AWS CloudWatch API Calls.** 121 | The cost of this resource will depend how much time the application is being used and modules as well. 122 | DBTop Monitoring Solution uses data extracted from DBEngine itself and only extract information on demand from AWS CloudWatch (GetMetricData API) in case the module is selected. 123 | AWS CloudWatch API Calls pricing can be review [here](https://aws.amazon.com/cloudwatch/pricing/). 124 | 125 | 126 | 127 | ## Solution Deployment 128 | 129 | > **Time to deploy:** Approximately 10 minutes. 130 | 131 | 132 | ### Create database monitoring users 133 | 134 | Database credentials are needed to connect to the database engine and gather real-time metrics, use following statements to create the monitoring users. 135 | 136 | 137 | #### [Amazon RDS for MySQL](https://aws.amazon.com/rds/mysql/) 138 | ```sql 139 | CREATE USER 'monitor'@'%' IDENTIFIED BY ''; 140 | GRANT PROCESS ON *.* TO 'monitor'@'%' ; 141 | ``` 142 | 143 | 144 | #### [Amazon RDS for PostgreSQL](https://aws.amazon.com/rds/postgresql/) 145 | ```sql 146 | CREATE USER monitor WITH PASSWORD ''; 147 | GRANT pg_read_all_stats TO monitor; 148 | ``` 149 | 150 | 151 | #### [Amazon RDS for SQLServer](https://aws.amazon.com/rds/sqlserver/) 152 | ```sql 153 | USE [master] 154 | GO 155 | CREATE LOGIN [monitor] WITH PASSWORD=N'', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=ON, CHECK_POLICY=ON 156 | GO 157 | use [master] 158 | GO 159 | GRANT CONNECT SQL TO [monitor] 160 | GO 161 | GRANT VIEW SERVER STATE TO [monitor] 162 | GO 163 | ``` 164 | 165 | 166 | #### [Amazon RDS for Oracle](https://aws.amazon.com/rds/oracle/) 167 | ```sql 168 | CREATE USER monitor IDENTIFIED BY ''; 169 | GRANT CREATE SESSION,SELECT ANY DICTIONARY TO monitor; 170 | ``` 171 | 172 | 173 | #### [Amazon DocumentDB (with MongoDB compatibility)](https://aws.amazon.com/documentdb/) 174 | ``` 175 | db.createUser( 176 | { 177 | user: "monitor", 178 | pwd: "", 179 | roles: [ "clusterMonitor" ] 180 | } 181 | ) 182 | ``` 183 | 184 | ### Launch CloudFormation Stack 185 | 186 | Follow the step-by-step instructions to configure and deploy the DBTop Monitoring Solution into your account. 187 | 188 | 1. Make sure you have sign in AWS Console already. 189 | 2. Download AWS Cloudformation Template ([DBMonitoringSolution.template](https://raw.githubusercontent.com/aws-samples/db-top-monitoring/main/conf/DBTopMonitoringSolution.template)) located into conf folder. 190 | 3. [**Open AWS CloudFormation Console**](https://console.aws.amazon.com/cloudformation/home#/stacks/create/template?stackName=DBTopMonitoringSolution) 191 | 4. Create an stack using Cloudformation template ([DBMonitoringSolution.template](/conf/DBMonitoringSolution.template)) already downloaded on step 2. 192 | 5. Input **Stack name** parameter. 193 | 6. Acknowledge **Application Updates - Disclaimer** parameter. 194 | 7. Input **Username** parameter, this username will be used to access the application. An email will be sent with temporary password from AWS Cognito Service. 195 | 8. Input **AWS Linux AMI** parameter, this parameter specify AWS AMI to build App EC2 Server. Keep default value. 196 | 9. Select **Instance Type** parameter, indicate what instance size is needed. 197 | 10. Select **VPC Name** parameter, indicate VPC to be used to deploy application server. 198 | 11. Select **Subnet Name** parameter, indicate subnet to be used to deploy application server, this subnet needs to have outbound internet access to reach AWS APIs. Also application server needs to be able to reach AWS Database Resources, add appropiate inboud rules on Amazon RDS security groups to allow network connections. 199 | 12. Select **Public IP Address** parameter, the deployment will assign private IP Address by default to access the application, you can assign Public IP Address to access the application in case you need it, Select (true) to assign Public IP Address. 200 | 13. Input **CIDR** parameter, specify CIDR inbound access rule, this will grant network access for the application. 201 | 14. Click **Next**, Click **Next**, select **acknowledge that AWS CloudFormation might create IAM resources with custom names**. and Click **Submit**. 202 | 15. Once Cloudformation has been deployed, gather application URL from output stack section. Username will be same you introduce on step 7 and temporary password will be sent by AWS Cognito Service. 203 | 16. Application deployment will take around 5 minutes to be completed. 204 | 205 | > **Note:** Because you are connecting to a site with a self-signed, untrusted host certificate, your browser may display a series of security warnings. 206 | Override the warnings and proceed to the site. To prevent site visitors from encountering warning screens, you must obtain a trusted, 207 | CA-signed certificate that not only encrypts, but also publicly authenticates you as the owner of the site. 208 | 209 | 210 | 211 | ## Security 212 | 213 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 214 | 215 | 216 | 217 | ## License 218 | 219 | This library is licensed under the MIT-0 License. See the [LICENSE](LICENSE.txt) file. 220 | 221 | -------------------------------------------------------------------------------- /conf/api.core.service: -------------------------------------------------------------------------------- 1 | #/usr/lib/systemd/system/api.core.service 2 | [Unit] 3 | Description=API Webservice - Core 4 | Documentation=https://apicore.aws 5 | After=network.target 6 | 7 | [Service] 8 | Environment=NODE_PORT=3002 9 | Type=simple 10 | User=ec2-user 11 | WorkingDirectory=/aws/apps/server/ 12 | ExecStart=/home/ec2-user/.nvm/versions/node/v23.4.0/bin/node api.core.js 13 | Restart=on-failure 14 | RestartSec=5s 15 | 16 | [Install] 17 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /conf/server.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | server_name _; 4 | ssl_protocols TLSv1.2; 5 | ssl_certificate /etc/nginx/ssl/server.crt; 6 | ssl_certificate_key /etc/nginx/ssl/server.key; 7 | root /aws/apps/frontend/www; 8 | index index.html; 9 | location / { 10 | try_files $uri /index.html; 11 | } 12 | location /api { 13 | proxy_pass http://127.0.0.1:3000/api; 14 | } 15 | } -------------------------------------------------------------------------------- /conf/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Verify code version 3 | version="$(curl https://version.code.ds.wwcs.aws.dev/?codeId=dbtop'&'moduleId=deploy)" 4 | 5 | #Install Software Packages 6 | # Bug : https://github.com/amazonlinux/amazon-linux-2023/issues/397 7 | while true; do 8 | echo "Trying to install rpm packages ..." 9 | sudo yum install -y openssl nginx && break 10 | done 11 | 12 | 13 | #Create Certificates 14 | sudo mkdir /etc/nginx/ssl/ 15 | sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.crt -subj "/C=US/ST=US/L=US/O=Global Security/OU=IT Department/CN=127.0.0.1" 16 | 17 | #Copy Configurations 18 | sudo cp conf/api.core.service /usr/lib/systemd/system/api.core.service 19 | sudo cp conf/server.conf /etc/nginx/conf.d/ 20 | 21 | #Enable Auto-Start 22 | sudo chkconfig nginx on 23 | sudo chkconfig api.core on 24 | 25 | #Change permissions 26 | sudo chown -R ec2-user:ec2-user /aws/apps 27 | 28 | #Copy aws-exports.json 29 | cp /aws/apps/conf/aws-exports.json /aws/apps/frontend/public/ 30 | cp /aws/apps/conf/aws-exports.json /aws/apps/server/ 31 | 32 | #Re-Start NGINX Services 33 | sudo service nginx restart 34 | 35 | 36 | #Download PEM Key for DocumentDB 37 | cd /aws/apps/server/; wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem 38 | 39 | 40 | 41 | #NodeJS Installation 42 | curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh --output install.sh 43 | sh install.sh 44 | . ~/.nvm/nvm.sh 45 | nvm install 23.4.0 46 | 47 | 48 | #NodeJS API Core Installation 49 | cd /aws/apps/server/; npm install; 50 | 51 | #Re-Start API Services 52 | sudo service api.core restart 53 | 54 | #React Application Installation 55 | cd /aws/apps/frontend/; npm install; npm run build; 56 | 57 | 58 | #Copy build content to www folder 59 | cp -r /aws/apps/frontend/build/* /aws/apps/frontend/www/ 60 | -------------------------------------------------------------------------------- /conf/update.sh: -------------------------------------------------------------------------------- 1 | #Load Profile 2 | version="$(curl https://version.code.ds.wwcs.aws.dev/?codeId=dbtop'&'moduleId=update)" 3 | source $HOME/.bash_profile 4 | 5 | #Clone Repository 6 | cd /tmp 7 | sudo rm -rf db-top-monitoring 8 | git clone https://github.com/GitHubRepository/db-top-monitoring.git 9 | cd db-top-monitoring 10 | sudo cp -r server frontend /aws/apps 11 | 12 | #Copy aws-exports.json 13 | cp /aws/apps/conf/aws-exports.json /aws/apps/frontend/public/ 14 | cp /aws/apps/conf/aws-exports.json /aws/apps/server/ 15 | 16 | 17 | #React Application Installation 18 | cd /aws/apps/frontend/; npm install; npm run build; 19 | 20 | #Copy build content to www folder 21 | cp -r /aws/apps/frontend/build/* /aws/apps/frontend/www/ 22 | 23 | #NodeJS API Core Installation 24 | cd /aws/apps/server/; npm install; 25 | 26 | #Re-Start API Services 27 | cat /aws/apps/frontend/public/version.json 28 | echo "Restarting the API Service..." 29 | sleep 10 30 | sudo service api.core restart 31 | 32 | -------------------------------------------------------------------------------- /docs/aurora.limitless.md: -------------------------------------------------------------------------------- 1 | # DBTop Monitoring Solution - Aurora Limitless Monitoring - Feature Release 2 | 3 | > **Disclaimer:** The sample code; software libraries; command line tools; proofs of concept; templates; or other related technology (including any of the foregoing that are provided by our personnel) is provided to you as AWS Content under the AWS Customer Agreement, or the relevant written agreement between you and AWS (whichever applies). You are responsible for testing, securing, and optimizing the AWS Content, such as sample code, as appropriate for production grade use based on your specific quality control practices and standards. Deploying AWS Content may incur AWS charges for creating or using AWS chargeable resources, such as running Amazon EC2 instances, using Amazon CloudWatch or Amazon Cognito. 4 | 5 | We're excited to announce a new feature release for DBTop Monitoring Solution an Open Source project that augment comprehensive monitoring capabilities for Amazon Aurora Limitless databases. This open-source enhancement enables database administrators and developers to gain deep visibility into Aurora Limitless infrastructure performance and resource utilization. 6 | 7 | image 8 | 9 | image 10 | 11 | 12 | ## About This Initiative 13 | 14 | This feature is part of an ongoing effort led by AWS Database Specialists to enhance the AWS Community's database monitoring capabilities. Their commitment to open-source development aims to provide advanced observability solutions for AWS database services, making it easier for customers to monitor, troubleshoot, and optimize their database deployments. 15 | 16 | The DBTop Monitoring Solution exemplifies AWS's commitment to open-source software, allowing customers to not only use but also customize and enhance the solution according to their specific needs. This collaboration between AWS Database Specialists and the community helps drive innovation in database monitoring and management. 17 | 18 | 19 | 20 | ## Key Features 21 | 22 | ### Real-time Router and Shard Monitoring 23 | 24 | * Live visualization of router performance metrics and traffic distribution 25 | * Detailed metrics for individual database shards including throughput, latency, and connection statistics 26 | * Dynamic topology view showing router-to-shard relationships and data flow 27 | 28 | ### CloudWatch Integration and Enhanced Monitoring 29 | 30 | * Aggregated CloudWatch metrics visualization 31 | * Enhanced monitoring integration providing OS-level metrics: 32 | * CPU utilization per process 33 | * Memory consumption patterns 34 | * I/O operations and throughput 35 | * Real-time process-level resource consumption analysis 36 | 37 | ### Storage Analysis 38 | 39 | * Comprehensive storage usage metrics at database level 40 | * Per-shard storage utilization 41 | * Data distribution visualization across shards. 42 | 43 | ## Feedback and Continuous Improvement 44 | 45 | At AWS, we are deeply committed to our Leadership Principle of Customer Obsession, and this open-source project is no exception. We are actively seeking feedback from our customers. 46 | 47 | 48 | ## Community Contribution 49 | 50 | We encourage community contributions to enhance this feature: 51 | 52 | * Additional metric collections 53 | * New visualization options 54 | * Custom analysis modules 55 | * Performance optimizations 56 | 57 | Submit pull requests or open issues on our GitHub repository to contribute or suggest improvements. 58 | 59 | 60 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@aws-amplify/ui-react": "^4.6.4", 7 | "@awsui/components-react": "^3.0.949", 8 | "@cloudscape-design/components": "^3.0.277", 9 | "@cloudscape-design/design-tokens": "^3.0.28", 10 | "@cloudscape-design/global-styles": "^1.0.9", 11 | "@testing-library/jest-dom": "^5.16.5", 12 | "@testing-library/react": "^13.4.0", 13 | "@testing-library/user-event": "^13.5.0", 14 | "axios": "^1.4.0", 15 | "crypto-js": "^4.1.1", 16 | "percentile": "^1.6.0", 17 | "react": "^18.2.0", 18 | "react-apexcharts": "^1.4.1", 19 | "react-dom": "^18.2.0", 20 | "react-router-dom": "^6.11.1", 21 | "web-vitals": "^2.1.4", 22 | "zustand": "^5.0.2" 23 | }, 24 | "devDependencies": { 25 | "react-scripts": "^5.0.1" 26 | }, 27 | "scripts": { 28 | "start": "react-scripts start", 29 | "build": "react-scripts build", 30 | "test": "react-scripts test", 31 | "eject": "react-scripts eject" 32 | }, 33 | "proxy": "http://34.192.180.1:3000", 34 | "eslintConfig": { 35 | "extends": [ 36 | "react-app", 37 | "react-app/jest" 38 | ] 39 | }, 40 | "browserslist": { 41 | "production": [ 42 | ">0.2%", 43 | "not dead", 44 | "not op_mini all" 45 | ], 46 | "development": [ 47 | "last 1 chrome version", 48 | "last 1 firefox version", 49 | "last 1 safari version" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 30 | DBTop Monitoring Solution 31 | 32 | 33 | 34 |
35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/public/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "0.1.5", 3 | "date":"2025-01-06" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/components/Animation01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import "../styles/css/animation.css"; 3 | 4 | const ChartAnimation = memo(({ speed="2s", rotate="0deg", width = "30px", height = "60px" }) => { 5 | 6 | const style = { 7 | "rotate": rotate, 8 | "width": width, 9 | "height": height, 10 | "border-radius": "5px", 11 | "display": "inline-block", 12 | "background": "linear-gradient(#fff 20%, #8ea9ff 60%, #fff 20%)", 13 | "background2": "linear-gradient(#f2f2f2 40%, #8ea9ff 30%, #f2f2f2 60%)", 14 | "background1": "linear-gradient(#8ea9ff 40%, #f2f2f2 30%, #f2f8ea9ff2f2 60%)", 15 | "background-size":"100% 200%", 16 | "background-repeat": "no-repeat", 17 | "animation": "placeholderAnimation " + speed + " infinite linear", 18 | }; 19 | 20 | return ( 21 |
22 |
23 |
24 |
25 | ); 26 | }); 27 | 28 | export default ChartAnimation; 29 | -------------------------------------------------------------------------------- /frontend/src/components/ChartArea01.js: -------------------------------------------------------------------------------- 1 | import {useState,useEffect} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | function ChartArea({series,serie,history, height, width="100%", title, colors=[]}) { 5 | 6 | const [chartData, setChartData] = useState(series); 7 | 8 | var options = { 9 | chart: { 10 | height: height, 11 | type: 'area', 12 | stacked: true, 13 | zoom: { 14 | enabled: false 15 | }, 16 | animations: { 17 | enabled: false, 18 | }, 19 | dynamicAnimation : 20 | { 21 | enabled: true, 22 | }, 23 | toolbar: { 24 | show: false, 25 | } 26 | 27 | }, 28 | colors: colors, 29 | markers: { 30 | size: 0, 31 | }, 32 | dataLabels: { 33 | enabled: false 34 | }, 35 | stroke: { 36 | curve: 'straight', 37 | width: 1, 38 | }, 39 | fill: { 40 | type: 'gradient', 41 | gradient: { 42 | opacityFrom: 0.6, 43 | opacityTo: 0.8, 44 | } 45 | }, 46 | title: { 47 | text : title, 48 | align: "center", 49 | show: false 50 | }, 51 | grid: { 52 | show: false, 53 | yaxis: { 54 | lines: { 55 | show: false 56 | } 57 | }, 58 | }, 59 | xaxis: { 60 | labels: { 61 | show: false, 62 | } 63 | }, 64 | yaxis: { 65 | tickAmount: 5, 66 | axisTicks: { 67 | show: true, 68 | }, 69 | axisBorder: { 70 | show: true, 71 | color: '#78909C', 72 | offsetX: 0, 73 | offsetY: 0 74 | }, 75 | min : 0, 76 | labels : { 77 | formatter: function(val, index) { 78 | 79 | if(val === 0) return '0'; 80 | if(val < 1000) return parseFloat(val).toFixed(1); 81 | 82 | var k = 1000, 83 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 84 | i = Math.floor(Math.log(val) / Math.log(k)); 85 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 86 | 87 | }, 88 | style: { 89 | colors: ['#C6C2C1'], 90 | fontSize: '11px', 91 | fontFamily: 'Lato', 92 | }, 93 | }, 94 | 95 | } 96 | }; 97 | 98 | // eslint-disable-next-line 99 | useEffect(() => { 100 | updateMetrics(); 101 | // eslint-disable-next-line react-hooks/exhaustive-deps 102 | }, [serie]); 103 | 104 | 105 | function updateMetrics(){ 106 | 107 | var currentData = []; 108 | var iCursor=0; 109 | chartData.forEach(function(item) { 110 | item.data.push(serie[iCursor] || null); 111 | if (item.data.length > history ){ 112 | item.data = item.data.slice(item.data.length-history) 113 | } 114 | currentData.push({name : item.name, data : item.data}); 115 | iCursor++; 116 | }) 117 | setChartData(currentData); 118 | 119 | } 120 | 121 | 122 | return ( 123 |
124 | 125 |
126 | ); 127 | } 128 | 129 | export default ChartArea; 130 | -------------------------------------------------------------------------------- /frontend/src/components/ChartArea02.js: -------------------------------------------------------------------------------- 1 | import {useState,useEffect,useRef} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | function ChartLine({series,history, height, width="100%", title, colors=[], border=2, timestamp}) { 5 | 6 | var options = { 7 | chart: { 8 | height: height, 9 | type: 'area', 10 | foreColor: '#C6C2C1', 11 | zoom: { 12 | enabled: false 13 | }, 14 | animations: { 15 | enabled: false, 16 | }, 17 | dynamicAnimation : 18 | { 19 | enabled: true, 20 | }, 21 | toolbar: { 22 | show: false, 23 | } 24 | 25 | },/* 26 | markers: { 27 | size: 4, 28 | strokeColors: '#29313e', 29 | },*/ 30 | dataLabels: { 31 | enabled: false 32 | }, 33 | colors: colors, 34 | stroke: { 35 | curve: 'smooth', 36 | width: 1 37 | }, 38 | markers: { 39 | size: 3, 40 | strokeWidth: 0, 41 | hover: { 42 | size: 9 43 | } 44 | }, 45 | fill: { 46 | type: "gradient", 47 | gradient: { 48 | shadeIntensity: 1, 49 | opacityFrom: 0.1, 50 | opacityTo: 0.8, 51 | stops: [0, 99, 100] 52 | } 53 | }, 54 | title: { 55 | text : title, 56 | align: "center", 57 | show: false, 58 | style: { 59 | fontSize: '12px', 60 | fontWeight: 'bold', 61 | fontFamily: "Lato", 62 | color : "#C6C2C1" 63 | } 64 | 65 | }, 66 | grid: { 67 | show: false, 68 | yaxis: { 69 | lines: { 70 | show: false 71 | } 72 | }, 73 | xaxis: { 74 | lines: { 75 | show: false 76 | } 77 | } 78 | }, 79 | tooltip: { 80 | theme: "dark", 81 | }, 82 | xaxis: { 83 | labels: { 84 | show: false, 85 | }, 86 | axisBorder: { 87 | show: false 88 | }, 89 | axisTicks: { 90 | show: true, 91 | borderType: 'solid', 92 | color: '#78909C', 93 | height: 4, 94 | offsetX: 0, 95 | offsetY: 0 96 | }, 97 | }, 98 | yaxis: { 99 | tickAmount: 5, 100 | axisTicks: { 101 | show: true, 102 | }, 103 | axisBorder: { 104 | show: true, 105 | color: '#78909C', 106 | offsetX: 0, 107 | offsetY: 0 108 | }, 109 | min : 0, 110 | labels : { 111 | formatter: function(val, index) { 112 | 113 | if(val === 0) return '0'; 114 | if(val < 1000) return parseFloat(val).toFixed(1); 115 | 116 | var k = 1000, 117 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 118 | i = Math.floor(Math.log(val) / Math.log(k)); 119 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 120 | 121 | }, 122 | style: { 123 | colors: ['#C6C2C1'], 124 | fontSize: '11px', 125 | fontFamily: 'Lato', 126 | }, 127 | }, 128 | 129 | } 130 | }; 131 | 132 | 133 | return ( 134 |
135 | 136 |
137 | ); 138 | } 139 | 140 | export default ChartLine; 141 | -------------------------------------------------------------------------------- /frontend/src/components/ChartArea03.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartLine = memo(({series, categories,history, height, width="100%", title, border=2, stacked=false }) => { 5 | 6 | var options = { 7 | chart: { 8 | height: height, 9 | type: 'area', 10 | foreColor: '#9e9b9a', 11 | stacked : stacked, 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: true, 17 | }, 18 | dynamicAnimation : 19 | { 20 | enabled: true, 21 | }, 22 | toolbar: { 23 | show: false, 24 | }, 25 | dropShadow: { 26 | enabled: false, 27 | top: 2, 28 | left: 2, 29 | blur: 4, 30 | opacity: 1, 31 | } 32 | 33 | }, 34 | markers: { 35 | size: 5, 36 | radius: 0, 37 | strokeWidth: 2, 38 | fillOpacity: 1, 39 | shape: "circle", 40 | }, 41 | dataLabels: { 42 | enabled: false 43 | }, 44 | legend: { 45 | show: true, 46 | showForSingleSeries: true, 47 | fontSize: '11px', 48 | fontFamily: 'Lato', 49 | }, 50 | theme: { 51 | palette : "palette2", 52 | monochrome: { 53 | enabled: true 54 | } 55 | }, 56 | stroke: { 57 | curve: 'straight', 58 | width: border 59 | }, 60 | title: { 61 | text : title, 62 | align: "center", 63 | show: false, 64 | style: { 65 | fontSize: '14px', 66 | fontWeight: 'bold', 67 | fontFamily: 'Lato', 68 | } 69 | 70 | }, 71 | grid: { 72 | show: false, 73 | yaxis: { 74 | lines: { 75 | show: false 76 | } 77 | }, 78 | xaxis: { 79 | lines: { 80 | show: false 81 | } 82 | } 83 | }, 84 | tooltip: { 85 | theme: "dark", 86 | x : { 87 | format: 'HH:mm', 88 | } 89 | }, 90 | xaxis: { 91 | type: 'datetime', 92 | labels: { 93 | format: 'HH:mm', 94 | style: { 95 | fontSize: '11px', 96 | fontFamily: 'Lato', 97 | }, 98 | } 99 | }, 100 | yaxis: { 101 | tickAmount: 5, 102 | axisTicks: { 103 | show: true, 104 | }, 105 | axisBorder: { 106 | show: true, 107 | color: '#78909C', 108 | offsetX: 0, 109 | offsetY: 0 110 | }, 111 | min : 0, 112 | labels : { 113 | formatter: function(val, index) { 114 | 115 | if(val === 0) return '0'; 116 | if(val < 1000) return parseFloat(val).toFixed(1); 117 | 118 | var k = 1000, 119 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 120 | i = Math.floor(Math.log(val) / Math.log(k)); 121 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 122 | 123 | }, 124 | style: { 125 | fontSize: '11px', 126 | fontFamily: 'Lato', 127 | }, 128 | }, 129 | 130 | } 131 | }; 132 | 133 | 134 | return ( 135 |
136 | 137 |
138 | ); 139 | }); 140 | 141 | export default ChartLine; 142 | -------------------------------------------------------------------------------- /frontend/src/components/ChartBar01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | 5 | const ChartBar = memo(({series,history, height, width="100%", title, colors=[], border=2, timestamp, stacked=false }) => { 6 | 7 | var options = { 8 | chart: { 9 | height: height, 10 | type: 'bar', 11 | stacked : stacked, 12 | foreColor: '#2ea597', 13 | zoom: { 14 | enabled: false 15 | }, 16 | animations: { 17 | enabled: true, 18 | easing: 'easeinout', 19 | }, 20 | dynamicAnimation : 21 | { 22 | enabled: true, 23 | }, 24 | toolbar: { 25 | show: false, 26 | } 27 | 28 | }, 29 | dataLabels: { 30 | enabled: false 31 | }, 32 | colors : colors, 33 | stroke: { 34 | curve: 'smooth', 35 | width: 1 36 | }, 37 | theme: { 38 | palette : "palette2" 39 | }, 40 | markers: { 41 | size: 3, 42 | strokeWidth: 0, 43 | hover: { 44 | size: 9 45 | } 46 | }, 47 | plotOptions: { 48 | bar: { 49 | borderRadius: 2, 50 | 51 | } 52 | }, 53 | tooltip: { 54 | theme: "dark", 55 | x : { 56 | format: 'HH:mm', 57 | } 58 | }, 59 | title: { 60 | text : title, 61 | align: "center", 62 | show: false, 63 | style: { 64 | fontSize: '14px', 65 | fontWeight: 'bold', 66 | fontFamily: "Lato", 67 | color : "#2ea597" 68 | } 69 | 70 | }, 71 | grid: { 72 | show: false, 73 | yaxis: { 74 | lines: { 75 | show: false 76 | } 77 | }, 78 | xaxis: { 79 | lines: { 80 | show: false 81 | } 82 | } 83 | }, 84 | xaxis: { 85 | type: 'datetime', 86 | labels: { 87 | format: 'HH:mm', 88 | style: { 89 | fontSize: '11px', 90 | fontFamily: 'Lato', 91 | }, 92 | } 93 | }, 94 | yaxis: { 95 | tickAmount: 5, 96 | axisTicks: { 97 | show: true, 98 | }, 99 | axisBorder: { 100 | show: true, 101 | color: '#78909C', 102 | offsetX: 0, 103 | offsetY: 0 104 | }, 105 | min : 0, 106 | labels : { 107 | formatter: function(val, index) { 108 | 109 | if(val === 0) return '0'; 110 | if(val < 1000) return parseFloat(val).toFixed(1); 111 | 112 | var k = 1000, 113 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 114 | i = Math.floor(Math.log(val) / Math.log(k)); 115 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 116 | 117 | }, 118 | style: { 119 | fontSize: '11px', 120 | fontFamily: 'Lato', 121 | }, 122 | }, 123 | 124 | } 125 | }; 126 | 127 | 128 | return ( 129 |
130 | 131 |
132 | ); 133 | }); 134 | 135 | export default ChartBar; 136 | -------------------------------------------------------------------------------- /frontend/src/components/ChartBar02.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | 5 | const ChartBar = memo(({ series, p50, p90, p95, avg, max, height, width="100%", title, colors=[], border=2 }) => { 6 | 7 | var options = { 8 | chart: { 9 | height: height, 10 | type: 'bar', 11 | foreColor: '#2ea597', 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: true, 17 | easing: 'easeinout', 18 | }, 19 | dynamicAnimation : 20 | { 21 | enabled: true, 22 | }, 23 | toolbar: { 24 | show: false, 25 | } 26 | 27 | }, 28 | annotations: { 29 | yaxis: [ 30 | { 31 | y: p90, 32 | strokeDashArray: 20, 33 | borderColor: 'red', 34 | label: { 35 | text: 'p90 : ' + CustomFormatNumberData(p90,2), 36 | offsetX : 950, 37 | offsetY : 9, 38 | position : "left", 39 | borderColor: 'red', 40 | style: { 41 | fontSize: '14px', 42 | fontWeight: 'bold', 43 | fontFamily: 'Lato', 44 | color: 'white', 45 | background: 'red', 46 | }, 47 | } 48 | }, 49 | { 50 | y: p95, 51 | strokeDashArray: 10, 52 | borderColor: 'gray', 53 | label: { 54 | text: 'p95 : ' + CustomFormatNumberData(p95,2), 55 | offsetX : 650, 56 | offsetY : 9, 57 | position : "left", 58 | borderColor: 'gray', 59 | style: { 60 | fontSize: '14px', 61 | fontWeight: 'bold', 62 | fontFamily: 'Lato', 63 | color: 'white', 64 | background: 'gray', 65 | }, 66 | } 67 | }, 68 | { 69 | y: avg, 70 | strokeDashArray: 10, 71 | borderColor: 'orange', 72 | label: { 73 | text: 'avg : ' + CustomFormatNumberData(avg,2), 74 | offsetX : 300, 75 | offsetY : 9, 76 | position : "left", 77 | borderColor: 'orange', 78 | style: { 79 | fontSize: '14px', 80 | fontWeight: 'bold', 81 | fontFamily: 'Lato', 82 | color: 'white', 83 | background: 'orange', 84 | }, 85 | } 86 | } 87 | ], 88 | }, 89 | dataLabels: { 90 | enabled: false 91 | }, 92 | legend: { 93 | show: true, 94 | showForSingleSeries: true, 95 | fontSize: '11px', 96 | fontFamily: 'Lato', 97 | }, 98 | colors : colors, 99 | stroke: { 100 | curve: 'smooth', 101 | width: 1 102 | }, 103 | markers: { 104 | size: 3, 105 | strokeWidth: 0, 106 | hover: { 107 | size: 9 108 | } 109 | }, 110 | tooltip: { 111 | theme: "dark" 112 | }, 113 | title: { 114 | text : title, 115 | align: "center", 116 | show: false, 117 | style: { 118 | fontSize: '14px', 119 | fontWeight: 'bold', 120 | fontFamily: "Lato", 121 | color : "#2ea597" 122 | } 123 | 124 | }, 125 | grid: { 126 | show: false, 127 | yaxis: { 128 | lines: { 129 | show: false 130 | } 131 | }, 132 | xaxis: { 133 | lines: { 134 | show: false 135 | } 136 | } 137 | }, 138 | xaxis: { 139 | labels: { 140 | show: false, 141 | }, 142 | }, 143 | yaxis: { 144 | tickAmount: 5, 145 | axisTicks: { 146 | show: true, 147 | }, 148 | axisBorder: { 149 | show: true, 150 | color: '#78909C', 151 | offsetX: 0, 152 | offsetY: 0 153 | }, 154 | min : 0, 155 | labels : { 156 | formatter: function(val, index) { 157 | 158 | if(val === 0) return '0'; 159 | if(val < 1000) return parseFloat(val).toFixed(1); 160 | 161 | var k = 1000, 162 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 163 | i = Math.floor(Math.log(val) / Math.log(k)); 164 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 165 | 166 | }, 167 | style: { 168 | fontSize: '11px', 169 | fontFamily: 'Lato', 170 | }, 171 | }, 172 | 173 | } 174 | }; 175 | 176 | 177 | function CustomFormatNumberData(value,decimalLength) { 178 | if(value == 0) return '0'; 179 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 180 | 181 | var k = 1024, 182 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 183 | i = Math.floor(Math.log(value) / Math.log(k)); 184 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 185 | } 186 | 187 | return ( 188 |
189 | 190 |
191 | ); 192 | }); 193 | 194 | export default ChartBar; 195 | -------------------------------------------------------------------------------- /frontend/src/components/ChartBar03.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | 5 | const ChartBar = memo(({series,history, height, width="100%", title, colors=[], border=2, timestamp }) => { 6 | 7 | var options = { 8 | chart: { 9 | height: height, 10 | type: 'bar', 11 | foreColor: '#2ea597', 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: false, 17 | easing: 'easeinout', 18 | }, 19 | dynamicAnimation : 20 | { 21 | enabled: true, 22 | }, 23 | toolbar: { 24 | show: false, 25 | } 26 | 27 | }, 28 | dataLabels: { 29 | enabled: false 30 | }, 31 | colors : colors, 32 | stroke: { 33 | curve: 'smooth', 34 | width: 1 35 | }, 36 | markers: { 37 | size: 3, 38 | strokeWidth: 0, 39 | hover: { 40 | size: 9 41 | } 42 | }, 43 | plotOptions: { 44 | bar: { 45 | borderRadius: 0 46 | } 47 | }, 48 | tooltip: { 49 | theme: "dark", 50 | x : { 51 | format: 'HH:mm', 52 | } 53 | }, 54 | title: { 55 | text : title, 56 | align: "center", 57 | show: false, 58 | style: { 59 | fontSize: '14px', 60 | fontWeight: 'bold', 61 | fontFamily: "Lato", 62 | color : "#2ea597" 63 | } 64 | 65 | }, 66 | grid: { 67 | show: false, 68 | yaxis: { 69 | lines: { 70 | show: false 71 | } 72 | }, 73 | xaxis: { 74 | lines: { 75 | show: false 76 | } 77 | } 78 | }, 79 | xaxis: { 80 | labels: { 81 | show: false, 82 | }, 83 | }, 84 | yaxis: { 85 | tickAmount: 5, 86 | axisTicks: { 87 | show: true, 88 | }, 89 | axisBorder: { 90 | show: true, 91 | color: '#78909C', 92 | offsetX: 0, 93 | offsetY: 0 94 | }, 95 | min : 0, 96 | labels : { 97 | formatter: function(val, index) { 98 | 99 | if(val === 0) return '0'; 100 | if(val < 1000) return parseFloat(val).toFixed(1); 101 | 102 | var k = 1000, 103 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 104 | i = Math.floor(Math.log(val) / Math.log(k)); 105 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 106 | 107 | }, 108 | style: { 109 | fontSize: '11px', 110 | fontFamily: 'Lato', 111 | }, 112 | }, 113 | 114 | } 115 | }; 116 | 117 | 118 | return ( 119 |
120 | 121 |
122 | ); 123 | }); 124 | 125 | export default ChartBar; 126 | -------------------------------------------------------------------------------- /frontend/src/components/ChartBar04.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | 5 | const ChartBar = memo(({series,history, height, width="100%", title, colors=[], border=2, timestamp }) => { 6 | 7 | var options = { 8 | chart: { 9 | height: height, 10 | foreColor: '#2ea597', 11 | zoom: { 12 | enabled: false 13 | }, 14 | animations: { 15 | enabled: true, 16 | easing: 'easeinout', 17 | }, 18 | dynamicAnimation : 19 | { 20 | enabled: true, 21 | }, 22 | toolbar: { 23 | show: false, 24 | } 25 | 26 | }, 27 | dataLabels: { 28 | enabled: false 29 | }, 30 | colors : colors, 31 | stroke: { 32 | curve: 'smooth', 33 | width: 1 34 | }, 35 | markers: { 36 | size: 15, 37 | radius: 2, 38 | strokeWidth: 5, 39 | fillOpacity: 2, 40 | shape: "circle", 41 | hover: { 42 | size: 9 43 | }, 44 | }, 45 | /*plotOptions: { 46 | bar: { 47 | borderRadius: 2, 48 | 49 | } 50 | },*/ 51 | theme: { 52 | monochrome: { 53 | enabled: true 54 | }, 55 | 56 | }, 57 | tooltip: { 58 | theme: "dark", 59 | x : { 60 | format: 'HH:mm', 61 | } 62 | }, 63 | title: { 64 | text : title, 65 | align: "center", 66 | show: false, 67 | style: { 68 | fontSize: '14px', 69 | fontWeight: 'bold', 70 | fontFamily: "Lato", 71 | color : "#2ea597" 72 | } 73 | 74 | }, 75 | grid: { 76 | show: false, 77 | yaxis: { 78 | lines: { 79 | show: false 80 | } 81 | }, 82 | xaxis: { 83 | lines: { 84 | show: false 85 | } 86 | } 87 | }, 88 | xaxis: { 89 | type: 'datetime', 90 | labels: { 91 | format: 'HH:mm', 92 | style: { 93 | fontSize: '11px', 94 | fontFamily: 'Lato', 95 | }, 96 | } 97 | }, 98 | yaxis: { 99 | tickAmount: 5, 100 | axisTicks: { 101 | show: true, 102 | }, 103 | axisBorder: { 104 | show: true, 105 | color: '#78909C', 106 | offsetX: 0, 107 | offsetY: 0 108 | }, 109 | min : 0, 110 | labels : { 111 | formatter: function(val, index) { 112 | 113 | if(val === 0) return '0'; 114 | if(val < 1000) return parseFloat(val).toFixed(1); 115 | 116 | var k = 1000, 117 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 118 | i = Math.floor(Math.log(val) / Math.log(k)); 119 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 120 | 121 | }, 122 | style: { 123 | fontSize: '11px', 124 | fontFamily: 'Lato', 125 | }, 126 | }, 127 | 128 | } 129 | }; 130 | 131 | 132 | return ( 133 |
134 | 135 |
136 | ); 137 | }); 138 | 139 | export default ChartBar; 140 | -------------------------------------------------------------------------------- /frontend/src/components/ChartBar05.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | 5 | const ChartBar = memo(({series,history, height, width="100%", title, colors=[], border=2, timestamp, maximum=100 }) => { 6 | 7 | var options = { 8 | chart: { 9 | height: height, 10 | type: 'bar', 11 | foreColor: '#2ea597', 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: true, 17 | easing: 'easeinout', 18 | }, 19 | dynamicAnimation : 20 | { 21 | enabled: true, 22 | }, 23 | toolbar: { 24 | show: false, 25 | } 26 | 27 | }, 28 | dataLabels: { 29 | enabled: false 30 | }, 31 | colors : colors, 32 | stroke: { 33 | curve: 'smooth', 34 | width: 1 35 | }, 36 | markers: { 37 | size: 3, 38 | strokeWidth: 0, 39 | hover: { 40 | size: 9 41 | } 42 | }, 43 | plotOptions: { 44 | bar: { 45 | borderRadius: 2, 46 | 47 | } 48 | }, 49 | tooltip: { 50 | theme: "dark", 51 | x : { 52 | format: 'HH:mm', 53 | } 54 | }, 55 | title: { 56 | text : title, 57 | align: "center", 58 | show: false, 59 | style: { 60 | fontSize: '14px', 61 | fontWeight: 'bold', 62 | fontFamily: "Lato", 63 | color : "#2ea597" 64 | } 65 | 66 | }, 67 | grid: { 68 | show: false, 69 | yaxis: { 70 | lines: { 71 | show: false 72 | } 73 | }, 74 | xaxis: { 75 | lines: { 76 | show: false 77 | } 78 | } 79 | }, 80 | xaxis: { 81 | type: 'datetime', 82 | labels: { 83 | format: 'HH:mm', 84 | style: { 85 | fontSize: '11px', 86 | fontFamily: 'Lato', 87 | }, 88 | } 89 | }, 90 | yaxis: { 91 | tickAmount: 5, 92 | axisTicks: { 93 | show: true, 94 | }, 95 | axisBorder: { 96 | show: true, 97 | color: '#78909C', 98 | offsetX: 0, 99 | offsetY: 0 100 | }, 101 | min : 0, 102 | max : maximum, 103 | labels : { 104 | formatter: function(val, index) { 105 | 106 | if(val === 0) return '0'; 107 | if(val < 1000) return parseFloat(val).toFixed(0); 108 | 109 | var k = 1000, 110 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 111 | i = Math.floor(Math.log(val) / Math.log(k)); 112 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 113 | 114 | }, 115 | style: { 116 | fontSize: '11px', 117 | fontFamily: 'Lato', 118 | }, 119 | }, 120 | 121 | }, 122 | annotations: { 123 | yaxis: [ 124 | { 125 | y: maximum, 126 | strokeDashArray: 10, 127 | borderColor: 'gray' 128 | }, 129 | ], 130 | }, 131 | }; 132 | 133 | function CustomFormatNumberData(value,decimalLength) { 134 | if(value == 0) return '0'; 135 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 136 | 137 | var k = 1024, 138 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 139 | i = Math.floor(Math.log(value) / Math.log(k)); 140 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 141 | } 142 | 143 | return ( 144 |
145 | 146 |
147 | ); 148 | }); 149 | 150 | export default ChartBar; 151 | -------------------------------------------------------------------------------- /frontend/src/components/ChartColumn01.js: -------------------------------------------------------------------------------- 1 | import Chart from 'react-apexcharts'; 2 | 3 | function ChartBar({ series, height, width="100%", title }) { 4 | 5 | var data = [{ 6 | data: series.data 7 | }]; 8 | 9 | var options = { 10 | chart: { 11 | type: 'bar', 12 | height: height, 13 | foreColor: '#9e9b9a', 14 | zoom: { 15 | enabled: false 16 | }, 17 | animations: { 18 | enabled: false, 19 | }, 20 | labels: series.categories, 21 | dynamicAnimation : 22 | { 23 | enabled: true, 24 | }, 25 | toolbar: { 26 | show: false, 27 | } 28 | }, 29 | plotOptions: { 30 | bar: { 31 | borderRadius: 4, 32 | horizontal: true, 33 | } 34 | }, 35 | xaxis: { 36 | //categories: series.categories, 37 | labels : { 38 | formatter: function(val, index) { 39 | 40 | if(val === 0) return '0'; 41 | if(val < 1000) return parseFloat(val).toFixed(0); 42 | 43 | var k = 1000, 44 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 45 | i = Math.floor(Math.log(val) / Math.log(k)); 46 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 47 | 48 | }, 49 | style: { 50 | fontSize: '11px', 51 | fontFamily: 'Lato', 52 | }, 53 | 54 | }, 55 | }, 56 | grid: { 57 | show: false, 58 | yaxis: { 59 | lines: { 60 | show: false 61 | } 62 | }, 63 | xaxis: { 64 | lines: { 65 | show: false 66 | }, 67 | } 68 | }, 69 | tooltip: { 70 | theme: "dark", 71 | }, 72 | dataLabels: { 73 | enabled: true, 74 | style: { 75 | fontSize: '11px', 76 | fontWeight: 'bold', 77 | fontFamily: 'Lato', 78 | }, 79 | formatter: function(val, index) { 80 | 81 | if(val === 0) return '0'; 82 | if(val < 1000) return parseFloat(val).toFixed(0); 83 | 84 | var k = 1000, 85 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 86 | i = Math.floor(Math.log(val) / Math.log(k)); 87 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 88 | 89 | }, 90 | }, 91 | 92 | }; 93 | 94 | 95 | return ( 96 |
97 | 98 |
99 | ); 100 | } 101 | 102 | export default ChartBar; 103 | -------------------------------------------------------------------------------- /frontend/src/components/ChartColumn02.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartBar = memo(({ series, categories, height, width="100%", title }) => { 5 | 6 | console.log(series,categories); 7 | var options = { 8 | chart: { 9 | type: 'bar', 10 | height: height, 11 | foreColor: '#9e9b9a', 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: false, 17 | }, 18 | labels: JSON.parse(categories), 19 | dynamicAnimation : 20 | { 21 | enabled: true, 22 | }, 23 | toolbar: { 24 | show: false, 25 | } 26 | }, 27 | plotOptions: { 28 | bar: { 29 | borderRadius: 4, 30 | horizontal: true, 31 | } 32 | }, 33 | xaxis: { 34 | categories: JSON.parse(categories), 35 | labels : { 36 | formatter: function(val, index) { 37 | 38 | if(val === 0) return '0'; 39 | if(val < 1000) return parseFloat(val).toFixed(0); 40 | 41 | var k = 1000, 42 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 43 | i = Math.floor(Math.log(val) / Math.log(k)); 44 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 45 | 46 | }, 47 | style: { 48 | fontSize: '11px', 49 | fontFamily: 'Lato', 50 | }, 51 | 52 | }, 53 | }, 54 | grid: { 55 | show: false, 56 | yaxis: { 57 | lines: { 58 | show: false 59 | } 60 | }, 61 | xaxis: { 62 | lines: { 63 | show: false 64 | }, 65 | } 66 | }, 67 | tooltip: { 68 | theme: "dark", 69 | }, 70 | dataLabels: { 71 | enabled: true, 72 | style: { 73 | fontSize: '11px', 74 | fontWeight: 'bold', 75 | fontFamily: 'Lato', 76 | }, 77 | formatter: function(val, index) { 78 | 79 | if(val === 0) return '0'; 80 | if(val < 1000) return parseFloat(val).toFixed(0); 81 | 82 | var k = 1000, 83 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 84 | i = Math.floor(Math.log(val) / Math.log(k)); 85 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 86 | 87 | }, 88 | }, 89 | 90 | }; 91 | 92 | 93 | return ( 94 |
95 | 96 |
97 | ); 98 | }); 99 | 100 | export default ChartBar; 101 | -------------------------------------------------------------------------------- /frontend/src/components/ChartLine02.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartLine = memo(({series,history, height, width="100%", title, border=2 }) => { 5 | 6 | var options = { 7 | chart: { 8 | height: height, 9 | type: 'line', 10 | foreColor: '#9e9b9a', 11 | zoom: { 12 | enabled: false 13 | }, 14 | animations: { 15 | enabled: false, 16 | }, 17 | dynamicAnimation : 18 | { 19 | enabled: true, 20 | }, 21 | toolbar: { 22 | show: false, 23 | }, 24 | dropShadow: { 25 | enabled: false, 26 | top: 2, 27 | left: 2, 28 | blur: 4, 29 | opacity: 1, 30 | } 31 | 32 | }, 33 | theme: { 34 | palette : "palette2" 35 | }, 36 | markers: { 37 | size: 5, 38 | radius: 0, 39 | strokeWidth: 2, 40 | fillOpacity: 1, 41 | shape: "circle", 42 | }, 43 | dataLabels: { 44 | enabled: false 45 | }, 46 | legend: { 47 | show: true, 48 | showForSingleSeries: true, 49 | fontSize: '11px', 50 | fontFamily: 'Lato', 51 | }, 52 | stroke: { 53 | curve: 'straight', 54 | width: border, 55 | 56 | }, 57 | title: { 58 | text : title, 59 | align: "center", 60 | show: false, 61 | style: { 62 | fontSize: '12px', 63 | fontWeight: 'bold', 64 | fontFamily: 'Lato', 65 | } 66 | 67 | }, 68 | grid: { 69 | show: false, 70 | yaxis: { 71 | lines: { 72 | show: false 73 | } 74 | }, 75 | xaxis: { 76 | lines: { 77 | show: false 78 | } 79 | } 80 | }, 81 | tooltip: { 82 | theme: "dark", 83 | }, 84 | xaxis: { 85 | labels: { 86 | show: false, 87 | }, 88 | axisBorder: { 89 | show: false 90 | }, 91 | axisTicks: { 92 | show: true, 93 | borderType: 'solid', 94 | color: '#78909C', 95 | height: 4, 96 | offsetX: 0, 97 | offsetY: 0 98 | }, 99 | }, 100 | yaxis: { 101 | tickAmount: 5, 102 | axisTicks: { 103 | show: true, 104 | }, 105 | axisBorder: { 106 | show: true, 107 | color: '#78909C', 108 | offsetX: 0, 109 | offsetY: 0 110 | }, 111 | min : 0, 112 | labels : { 113 | formatter: function(val, index) { 114 | 115 | if(val === 0) return '0'; 116 | if(val < 1000) return parseFloat(val).toFixed(1); 117 | 118 | var k = 1000, 119 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 120 | i = Math.floor(Math.log(val) / Math.log(k)); 121 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 122 | 123 | }, 124 | style: { 125 | fontSize: '11px', 126 | fontFamily: 'Lato', 127 | }, 128 | }, 129 | 130 | } 131 | }; 132 | 133 | 134 | return ( 135 |
136 | 137 |
138 | ); 139 | }); 140 | 141 | export default ChartLine; 142 | -------------------------------------------------------------------------------- /frontend/src/components/ChartLine03.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartLine = memo(({series,history, height, width="100%", title, border=2 }) => { 5 | 6 | var options = { 7 | chart: { 8 | height: height, 9 | type: 'line', 10 | foreColor: '#9e9b9a', 11 | zoom: { 12 | enabled: false 13 | }, 14 | animations: { 15 | enabled: false, 16 | }, 17 | dynamicAnimation : 18 | { 19 | enabled: true, 20 | }, 21 | toolbar: { 22 | show: false, 23 | }, 24 | dropShadow: { 25 | enabled: false, 26 | top: 2, 27 | left: 2, 28 | blur: 4, 29 | opacity: 1, 30 | } 31 | 32 | }, 33 | theme: { 34 | palette : "palette2" 35 | }, 36 | markers: { 37 | size: 4, 38 | radius: 0, 39 | strokeWidth: 0.1, 40 | fillOpacity: 1, 41 | shape: "circle", 42 | }, 43 | dataLabels: { 44 | enabled: false 45 | }, 46 | legend: { 47 | show: true, 48 | showForSingleSeries: true, 49 | fontSize: '11px', 50 | fontFamily: 'Lato', 51 | }, 52 | stroke: { 53 | curve: 'straight', 54 | width: border, 55 | dashArray: [5,5,5] 56 | }, 57 | title: { 58 | text : title, 59 | align: "center", 60 | show: false, 61 | style: { 62 | fontSize: '12px', 63 | fontWeight: 'bold', 64 | fontFamily: "Lato", 65 | } 66 | 67 | }, 68 | grid: { 69 | show: false, 70 | yaxis: { 71 | lines: { 72 | show: false 73 | } 74 | }, 75 | xaxis: { 76 | lines: { 77 | show: false 78 | } 79 | } 80 | }, 81 | tooltip: { 82 | theme: "dark", 83 | }, 84 | xaxis: { 85 | labels: { 86 | show: false, 87 | }, 88 | axisBorder: { 89 | show: false 90 | }, 91 | axisTicks: { 92 | show: true, 93 | borderType: 'solid', 94 | color: '#78909C', 95 | height: 4, 96 | offsetX: 0, 97 | offsetY: 0 98 | }, 99 | }, 100 | yaxis: { 101 | tickAmount: 5, 102 | axisTicks: { 103 | show: true, 104 | }, 105 | axisBorder: { 106 | show: true, 107 | color: '#78909C', 108 | offsetX: 0, 109 | offsetY: 0 110 | }, 111 | min : 0, 112 | labels : { 113 | formatter: function(val, index) { 114 | 115 | if(val === 0) return '0'; 116 | if(val < 1000) return parseFloat(val).toFixed(1); 117 | 118 | var k = 1000, 119 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 120 | i = Math.floor(Math.log(val) / Math.log(k)); 121 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 122 | 123 | }, 124 | style: { 125 | fontSize: '11px', 126 | fontFamily: 'Lato', 127 | }, 128 | }, 129 | } 130 | }; 131 | 132 | 133 | return ( 134 |
135 | 136 |
137 | ); 138 | }); 139 | 140 | export default ChartLine; 141 | -------------------------------------------------------------------------------- /frontend/src/components/ChartLine04.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartLine = memo(({series, categories,history, height, width="100%", title, border=2, stacked = false }) => { 5 | 6 | var options = { 7 | chart: { 8 | height: height, 9 | type: 'line', 10 | foreColor: '#9e9b9a', 11 | stacked : stacked, 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: true, 17 | }, 18 | dynamicAnimation : 19 | { 20 | enabled: true, 21 | }, 22 | toolbar: { 23 | show: false, 24 | }, 25 | dropShadow: { 26 | enabled: false, 27 | top: 2, 28 | left: 2, 29 | blur: 4, 30 | opacity: 1, 31 | } 32 | 33 | }, 34 | markers: { 35 | size: 5, 36 | radius: 0, 37 | strokeWidth: 2, 38 | fillOpacity: 1, 39 | shape: "circle", 40 | }, 41 | dataLabels: { 42 | enabled: false 43 | }, 44 | legend: { 45 | show: true, 46 | showForSingleSeries: true, 47 | fontSize: '11px', 48 | fontFamily: 'Lato', 49 | }, 50 | theme: { 51 | palette : "palette2" 52 | }, 53 | stroke: { 54 | curve: 'straight', 55 | width: border 56 | }, 57 | title: { 58 | text : title, 59 | align: "center", 60 | show: false, 61 | style: { 62 | fontSize: '14px', 63 | fontWeight: 'bold', 64 | fontFamily: 'Lato', 65 | } 66 | 67 | }, 68 | grid: { 69 | show: false, 70 | yaxis: { 71 | lines: { 72 | show: false 73 | } 74 | }, 75 | xaxis: { 76 | lines: { 77 | show: false 78 | } 79 | } 80 | }, 81 | tooltip: { 82 | theme: "dark", 83 | x : { 84 | format: 'HH:mm', 85 | } 86 | }, 87 | xaxis: { 88 | type: 'datetime', 89 | labels: { 90 | format: 'HH:mm', 91 | style: { 92 | fontSize: '11px', 93 | fontFamily: 'Lato', 94 | }, 95 | } 96 | }, 97 | yaxis: { 98 | tickAmount: 5, 99 | axisTicks: { 100 | show: true, 101 | }, 102 | axisBorder: { 103 | show: true, 104 | color: '#78909C', 105 | offsetX: 0, 106 | offsetY: 0 107 | }, 108 | min : 0, 109 | labels : { 110 | formatter: function(val, index) { 111 | 112 | if(val === 0) return '0'; 113 | if(val < 1000) return parseFloat(val).toFixed(1); 114 | 115 | var k = 1000, 116 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 117 | i = Math.floor(Math.log(val) / Math.log(k)); 118 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 119 | 120 | }, 121 | style: { 122 | fontSize: '11px', 123 | fontFamily: 'Lato', 124 | }, 125 | }, 126 | 127 | } 128 | }; 129 | 130 | 131 | return ( 132 |
133 | 134 |
135 | ); 136 | }); 137 | 138 | export default ChartLine; 139 | -------------------------------------------------------------------------------- /frontend/src/components/ChartLine05.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartLine = memo(({ series, p50, p90, p95, avg, max, height, width="100%", title, border=2 }) => { 5 | 6 | 7 | var options = { 8 | chart: { 9 | height: height, 10 | type: 'line', 11 | foreColor: '#9e9b9a', 12 | zoom: { 13 | enabled: false 14 | }, 15 | animations: { 16 | enabled: true, 17 | }, 18 | dynamicAnimation : 19 | { 20 | enabled: true, 21 | }, 22 | toolbar: { 23 | show: false, 24 | }, 25 | dropShadow: { 26 | enabled: false, 27 | top: 2, 28 | left: 2, 29 | blur: 4, 30 | opacity: 1, 31 | } 32 | 33 | }, 34 | annotations: { 35 | yaxis: [ 36 | { 37 | y: p90, 38 | strokeDashArray: 20, 39 | borderColor: 'red', 40 | label: { 41 | text: 'p90 : ' + CustomFormatNumberData(p90,2), 42 | offsetX : 900, 43 | offsetY : 9, 44 | position : "left", 45 | borderColor: 'red', 46 | style: { 47 | fontSize: '14px', 48 | fontWeight: 'bold', 49 | fontFamily: 'Lato', 50 | color: 'white', 51 | background: 'red', 52 | }, 53 | } 54 | }, 55 | { 56 | y: p95, 57 | strokeDashArray: 10, 58 | borderColor: 'gray', 59 | label: { 60 | text: 'p95 : ' + CustomFormatNumberData(p95,2), 61 | offsetX : 600, 62 | offsetY : 9, 63 | position : "left", 64 | borderColor: 'gray', 65 | style: { 66 | fontSize: '14px', 67 | fontWeight: 'bold', 68 | fontFamily: 'Lato', 69 | color: 'white', 70 | background: 'gray', 71 | }, 72 | } 73 | }, 74 | { 75 | y: avg, 76 | strokeDashArray: 10, 77 | borderColor: 'orange', 78 | label: { 79 | text: 'avg : ' + CustomFormatNumberData(avg,2), 80 | offsetX : 300, 81 | offsetY : 9, 82 | position : "left", 83 | borderColor: 'orange', 84 | style: { 85 | fontSize: '14px', 86 | fontWeight: 'bold', 87 | fontFamily: 'Lato', 88 | color: 'white', 89 | background: 'orange', 90 | }, 91 | } 92 | } 93 | ], 94 | }, 95 | markers: { 96 | size: 4, 97 | radius: 0, 98 | strokeWidth: 0.1, 99 | fillOpacity: 1, 100 | shape: "circle", 101 | }, 102 | dataLabels: { 103 | enabled: false 104 | }, 105 | legend: { 106 | show: true, 107 | showForSingleSeries: true, 108 | fontSize: '11px', 109 | fontFamily: 'Lato', 110 | }, 111 | stroke: { 112 | curve: 'straight', 113 | width: border 114 | }, 115 | title: { 116 | text : title, 117 | align: "center", 118 | show: false, 119 | style: { 120 | fontSize: '14px', 121 | fontWeight: 'bold', 122 | fontFamily: 'Lato', 123 | } 124 | 125 | }, 126 | grid: { 127 | show: false, 128 | yaxis: { 129 | lines: { 130 | show: false 131 | } 132 | }, 133 | xaxis: { 134 | lines: { 135 | show: false 136 | } 137 | } 138 | }, 139 | tooltip: { 140 | theme: "dark", 141 | x : { 142 | format: 'HH:mm', 143 | } 144 | }, 145 | xaxis: { 146 | type: 'datetime', 147 | labels: { 148 | format: 'HH:mm', 149 | style: { 150 | fontSize: '11px', 151 | fontFamily: 'Lato', 152 | }, 153 | } 154 | }, 155 | yaxis: { 156 | tickAmount: 5, 157 | axisTicks: { 158 | show: true, 159 | }, 160 | axisBorder: { 161 | show: true, 162 | color: '#78909C', 163 | offsetX: 0, 164 | offsetY: 0 165 | }, 166 | min : 0, 167 | labels : { 168 | formatter: function(val, index) { 169 | 170 | if(val === 0) return '0'; 171 | if(val < 1000) return parseFloat(val).toFixed(1); 172 | 173 | var k = 1000, 174 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 175 | i = Math.floor(Math.log(val) / Math.log(k)); 176 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 177 | 178 | }, 179 | style: { 180 | fontSize: '11px', 181 | fontFamily: 'Lato', 182 | }, 183 | }, 184 | 185 | } 186 | }; 187 | 188 | 189 | function CustomFormatNumberData(value,decimalLength) { 190 | if(value == 0) return '0'; 191 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 192 | 193 | var k = 1024, 194 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 195 | i = Math.floor(Math.log(value) / Math.log(k)); 196 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 197 | } 198 | 199 | return ( 200 |
201 | 202 |
203 | ); 204 | }); 205 | 206 | export default ChartLine; 207 | 208 | 209 | -------------------------------------------------------------------------------- /frontend/src/components/ChartPie-01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartComponent = memo(({ series, labels, title="", height="350px", width="100%" }) => { 5 | 6 | 7 | var options = { 8 | chart: { 9 | type: 'donut', 10 | foreColor: '#9e9b9a', 11 | zoom: { 12 | enabled: true, 13 | }, 14 | }, 15 | labels: labels, 16 | stroke: { 17 | colors: ['#fff'] 18 | }, 19 | fill: { 20 | opacity: 0.8 21 | }, 22 | theme: { 23 | monochrome: { 24 | enabled: true 25 | } 26 | }, 27 | title: { 28 | text : title, 29 | align: "center", 30 | show: false, 31 | style: { 32 | fontSize: '14px', 33 | fontWeight: 'bold', 34 | fontFamily: 'Lato', 35 | } 36 | 37 | }, 38 | legend: { 39 | show: true, 40 | showForSingleSeries: true, 41 | fontSize: '11px', 42 | fontFamily: 'Lato', 43 | position: 'bottom' 44 | }, 45 | responsive: [{ 46 | breakpoint: 480, 47 | options: { 48 | chart: { 49 | width: 200 50 | }, 51 | legend: { 52 | position: 'bottom' 53 | } 54 | } 55 | }], 56 | yaxis: { 57 | tickAmount: 5, 58 | axisTicks: { 59 | show: true, 60 | }, 61 | axisBorder: { 62 | show: true, 63 | color: '#78909C', 64 | offsetX: 0, 65 | offsetY: 0 66 | }, 67 | min : 0, 68 | labels : { 69 | formatter: function(val, index) { 70 | 71 | if(val === 0) return '0'; 72 | if(val < 1000) return parseFloat(val).toFixed(1); 73 | 74 | var k = 1000, 75 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 76 | i = Math.floor(Math.log(val) / Math.log(k)); 77 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 78 | 79 | }, 80 | style: { 81 | fontSize: '11px', 82 | fontFamily: 'Lato', 83 | }, 84 | }, 85 | }, 86 | dataLabels: { 87 | formatter(val, opts) { 88 | const name = opts.w.globals.labels[opts.seriesIndex] 89 | return [name, val.toFixed(1) + '%'] 90 | }, 91 | }, 92 | }; 93 | 94 | 95 | return ( 96 |
97 | 98 |
99 | ); 100 | }); 101 | 102 | export default ChartComponent; 103 | -------------------------------------------------------------------------------- /frontend/src/components/ChartPolar-01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartComponent = memo(({ series, labels, title="", height="350px", width="100%", onClickEvent }) => { 5 | 6 | function onClickChart(object){ 7 | onClickEvent(object); 8 | } 9 | 10 | var options = { 11 | chart: { 12 | type: 'polarArea', 13 | foreColor: '#9e9b9a', 14 | zoom: { 15 | enabled: true, 16 | }, 17 | events: { 18 | dataPointSelection: function(event, chartContext, config) { 19 | onClickChart({ selectedItem : config.dataPointIndex } ); 20 | } 21 | 22 | }, 23 | }, 24 | title: { 25 | text : title, 26 | align: "right", 27 | show: false, 28 | style: { 29 | fontSize: '14px', 30 | fontWeight: 'bold', 31 | fontFamily: "Lato", 32 | color : "#2ea597" 33 | } 34 | 35 | }, 36 | theme: { 37 | palette : "palette2", 38 | monochrome: { 39 | enabled: true 40 | } 41 | }, 42 | labels: labels, 43 | stroke: { 44 | colors: ['#fff'] 45 | }, 46 | fill: { 47 | opacity: 0.8 48 | }, 49 | legend: { 50 | show: true, 51 | showForSingleSeries: true, 52 | fontSize: '11px', 53 | fontFamily: 'Lato', 54 | }, 55 | responsive: [{ 56 | breakpoint: 480, 57 | options: { 58 | legend: { 59 | position: 'bottom' 60 | } 61 | } 62 | }], 63 | yaxis: { 64 | tickAmount: 3, 65 | axisTicks: { 66 | show: true, 67 | }, 68 | axisBorder: { 69 | show: true, 70 | color: '#78909C', 71 | offsetX: 0, 72 | offsetY: 0 73 | }, 74 | min : 0, 75 | labels : { 76 | formatter: function(val, index) { 77 | 78 | if(val === 0) return '0'; 79 | if(val < 1000) return parseFloat(val).toFixed(1); 80 | 81 | var k = 1000, 82 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 83 | i = Math.floor(Math.log(val) / Math.log(k)); 84 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 85 | 86 | }, 87 | style: { 88 | fontSize: '11px', 89 | fontFamily: 'Lato', 90 | }, 91 | }, 92 | } 93 | }; 94 | 95 | 96 | return ( 97 |
98 | 99 |
100 | ); 101 | }); 102 | 103 | export default ChartComponent; 104 | -------------------------------------------------------------------------------- /frontend/src/components/ChartPolar-02.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartComponent = memo(({ series, labels, title="", height="350px", width="100%"}) => { 5 | 6 | var options = { 7 | chart: { 8 | type: 'polarArea', 9 | foreColor: '#9e9b9a', 10 | zoom: { 11 | enabled: true, 12 | }, 13 | }, 14 | title: { 15 | text : title, 16 | align: "right", 17 | show: false, 18 | style: { 19 | fontSize: '14px', 20 | fontWeight: 'bold', 21 | fontFamily: "Lato", 22 | color : "#2ea597" 23 | } 24 | 25 | }, 26 | theme: { 27 | palette : "palette2", 28 | monochrome: { 29 | enabled: true 30 | } 31 | }, 32 | labels: JSON.parse(labels), 33 | stroke: { 34 | colors: ['#fff'] 35 | }, 36 | fill: { 37 | opacity: 0.8 38 | }, 39 | legend: { 40 | show: true, 41 | showForSingleSeries: true, 42 | fontSize: '11px', 43 | fontFamily: 'Lato', 44 | }, 45 | responsive: [{ 46 | breakpoint: 480, 47 | options: { 48 | legend: { 49 | position: 'bottom' 50 | } 51 | } 52 | }], 53 | yaxis: { 54 | tickAmount: 3, 55 | axisTicks: { 56 | show: true, 57 | }, 58 | axisBorder: { 59 | show: true, 60 | color: '#78909C', 61 | offsetX: 0, 62 | offsetY: 0 63 | }, 64 | min : 0, 65 | labels : { 66 | formatter: function(val, index) { 67 | 68 | if(val === 0) return '0'; 69 | if(val < 1000) return parseFloat(val).toFixed(1); 70 | 71 | var k = 1000, 72 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 73 | i = Math.floor(Math.log(val) / Math.log(k)); 74 | return parseFloat((val / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; 75 | 76 | }, 77 | style: { 78 | fontSize: '11px', 79 | fontFamily: 'Lato', 80 | }, 81 | }, 82 | } 83 | }; 84 | 85 | 86 | return ( 87 |
88 | 89 |
90 | ); 91 | }); 92 | 93 | export default ChartComponent; 94 | -------------------------------------------------------------------------------- /frontend/src/components/ChartProgressBar-01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | 3 | const Metric = memo(({ value, valueSufix, title, precision, format=1, height="10px", fontSizeTitle = "11px", fontSizeValue = "22px", fontSizeValueSufix = "12px", fontColorTitle = "#2ea597", fontColorValue = "orange", chartColor="#8ea9ff" }) => { 4 | 5 | var counterValue = 0; 6 | if (value > 100){ 7 | chartColor = "red"; 8 | } 9 | 10 | 11 | try { 12 | switch (format) { 13 | case 1: 14 | counterValue = CustomFormatNumberRaw(value,precision); 15 | break; 16 | 17 | case 2: 18 | counterValue = CustomFormatNumberData(value,precision); 19 | break; 20 | 21 | case 3: 22 | counterValue = CustomFormatNumberRawInteger(value,0); 23 | break; 24 | 25 | } 26 | 27 | } 28 | catch{ 29 | console.log('error'); 30 | } 31 | 32 | 33 | 34 | function CustomFormatNumberData(value,decimalLength) { 35 | if(value == 0) return '0'; 36 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 37 | 38 | var k = 1024, 39 | sizes = ['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZT', 'YB'], 40 | i = Math.floor(Math.log(value) / Math.log(k)); 41 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 42 | } 43 | 44 | 45 | function CustomFormatNumberRaw(value,decimalLength) { 46 | if (value < 100 && decimalLength == 0 ) 47 | decimalLength=2; 48 | 49 | if (value==0) 50 | decimalLength=0; 51 | 52 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 53 | 54 | } 55 | 56 | function CustomFormatNumberRawInteger(value,decimalLength) { 57 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 58 | } 59 | 60 | return ( 61 |
62 | 63 | {counterValue} 64 | 65 | 66 |  {valueSufix} 67 | 68 |
69 |
70 |
= 100 ? "100%" : (String( (Math.trunc( parseFloat(value) ) || 0 ) ) + "%" ) ) }}>
71 |
72 | 73 | {title} 74 | 75 |
76 | ) 77 | }); 78 | 79 | export default Metric 80 | 81 | -------------------------------------------------------------------------------- /frontend/src/components/ChartRadialBar01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartRadial = memo(({ series, height, width, title, fontSizeTitle = "11px", fontSizeValue = "22px", fontColorTitle = "#C6C2C1", fontColorValue = "orange" }) => { 5 | 6 | var options = { 7 | chart: { 8 | type: 'radialBar', 9 | foreColor: '#C6C2C1', 10 | }, 11 | plotOptions: { 12 | radialBar: { 13 | hollow: { 14 | size: '70%', 15 | } 16 | }, 17 | }, 18 | labels: [title], 19 | }; 20 | 21 | return ( 22 |
23 |
24 | 25 |
26 |
27 | 28 | ); 29 | }); 30 | 31 | export default ChartRadial; 32 | -------------------------------------------------------------------------------- /frontend/src/components/ChartSparkline01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | import Chart from 'react-apexcharts'; 3 | 4 | const ChartLine = memo(({series, height="60px", width="100%", title, border=2, type="line" }) => { 5 | 6 | var options = { 7 | chart: { 8 | type: type, 9 | sparkline: { 10 | enabled: true 11 | }, 12 | }, 13 | stroke: { 14 | curve: 'straight' 15 | }, 16 | fill: { 17 | opacity: 0.3, 18 | }, 19 | yaxis: { 20 | min: 0 21 | }, 22 | }; 23 | 24 | 25 | return ( 26 |
27 | 28 |
29 | ); 30 | }); 31 | 32 | export default ChartLine; 33 | -------------------------------------------------------------------------------- /frontend/src/components/Functions.js: -------------------------------------------------------------------------------- 1 | import { configuration } from '../pages/Configs'; 2 | import { createSearchParams } from "react-router-dom"; 3 | import Box from "@awsui/components-react/box"; 4 | 5 | 6 | //--++ Version Functions 7 | 8 | export async function applicationVersionUpdate(params) { 9 | var version = await gatherVersionJsonFile(params); 10 | return version; 11 | } 12 | 13 | 14 | const gatherVersionJsonFile = async (params) => { 15 | var json = { release : "0.0.0", date : "2023-09-01"} 16 | try { 17 | const response = await fetch(configuration["apps-settings"]["version-code-url"] 18 | + '?' + createSearchParams({ 19 | codeId: params.codeId, 20 | moduleId: params.moduleId 21 | }).toString() 22 | ); 23 | json = await response.json(); 24 | } 25 | catch{ 26 | 27 | } 28 | return(json); 29 | } 30 | 31 | 32 | //-- Version Functions 33 | export const gatherLocalVersion = async () => { 34 | var json = { release : "0.0.0", date : "2023-09-01"} 35 | try { 36 | const response = await fetch("/version.json"); 37 | json = await response.json(); 38 | } 39 | catch{ 40 | 41 | } 42 | return(json); 43 | } 44 | 45 | 46 | //--++ Custom Format Functions 47 | 48 | export function customFormatNumber(value,decimalLength) { 49 | if(value == 0) return '0'; 50 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 51 | 52 | var k = 1024, 53 | sizes = ['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 54 | i = Math.floor(Math.log(value) / Math.log(k)); 55 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 56 | } 57 | 58 | 59 | export function customFormatNumberLong(value,decimalLength) { 60 | if(value == 0) return '0'; 61 | if(value < 1000) return parseFloat(value).toFixed(decimalLength); 62 | 63 | var k = 1000, 64 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 65 | i = Math.floor(Math.log(value) / Math.log(k)); 66 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 67 | } 68 | 69 | 70 | export function customFormatNumberShort(value,decimalLength) { 71 | if (value < 100 && decimalLength == 0 ) 72 | decimalLength=2; 73 | 74 | if (value==0) 75 | decimalLength=0; 76 | 77 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 78 | 79 | } 80 | 81 | 82 | export function customFormatNumberInteger(value) { 83 | 84 | return value.toLocaleString('en-US', {minimumFractionDigits:0, maximumFractionDigits:0}); 85 | 86 | } 87 | 88 | export class classMetric { 89 | 90 | 91 | constructor(arrayMetrics) { 92 | 93 | this.dataHistory = {}; 94 | 95 | arrayMetrics.forEach(metric => { 96 | 97 | this.dataHistory = {...this.dataHistory, [metric.name]: { name : metric.name, history : metric.history, data : Array(50).fill(null) } } 98 | 99 | }); 100 | 101 | 102 | } 103 | 104 | init(currentObject,currentTime,oldObject,oldTime) { 105 | this.currentObject = currentObject; 106 | this.currentTime = currentTime; 107 | this.oldObject = oldObject; 108 | this.oldTime = oldTime; 109 | } 110 | 111 | newSnapshot(currentObject,currentTime) { 112 | 113 | this.oldObject = this.currentObject; 114 | this.oldTime = this.currentTime; 115 | 116 | this.currentObject = currentObject; 117 | this.currentTime = currentTime; 118 | 119 | } 120 | 121 | 122 | getDelta(propertyName) { 123 | try 124 | { 125 | return ( 126 | ( 127 | (this.currentObject[propertyName]['Value'] - this.oldObject[propertyName]['Value']) / 128 | (Math.abs(this.currentTime - this.oldTime) / 1000) 129 | ) || 0 130 | ) ; 131 | } 132 | catch(e) { 133 | return 0; 134 | } 135 | } 136 | 137 | getDeltaByValue(propertyName,propertyValue) { 138 | try 139 | { 140 | return ( 141 | ( 142 | (this.currentObject[propertyName][propertyValue] - this.oldObject[propertyName][propertyValue]) / 143 | (Math.abs(this.currentTime - this.oldTime) / 1000) 144 | ) || 0 145 | ) ; 146 | } 147 | catch(e) { 148 | return 0; 149 | } 150 | } 151 | 152 | getDeltaByIndex(propertyName) { 153 | try 154 | { 155 | return ( 156 | ( 157 | (this.currentObject[propertyName] - this.oldObject[propertyName]) / 158 | (Math.abs(this.currentTime - this.oldTime) / 1000) 159 | ) || 0 160 | ) ; 161 | } 162 | catch(e) { 163 | return 0; 164 | } 165 | } 166 | 167 | getValue(propertyName) { 168 | try 169 | { 170 | return (this.currentObject[propertyName]['Value']) ; 171 | } 172 | catch(e) { 173 | return 0; 174 | } 175 | } 176 | 177 | getValueByValue(propertyName,propertyValue) { 178 | try 179 | { 180 | return (this.currentObject[propertyName][propertyValue]) ; 181 | } 182 | catch(e) { 183 | return 0; 184 | } 185 | } 186 | 187 | getValueByIndex(propertyName) { 188 | try 189 | { 190 | return (this.currentObject[propertyName]) ; 191 | } 192 | catch(e) { 193 | return 0; 194 | } 195 | } 196 | 197 | addProperty(propertyName) 198 | { 199 | this.dataHistory = {...this.dataHistory, [propertyName]: Array(50).fill(null) } 200 | } 201 | 202 | getPropertyValues (propertyName){ 203 | return this.dataHistory[propertyName]; 204 | } 205 | 206 | 207 | addPropertyValue (propertyName,propertyValue){ 208 | this.dataHistory[propertyName].data.push(propertyValue); 209 | this.dataHistory[propertyName].data = this.dataHistory[propertyName].data.slice(this.dataHistory[propertyName].data.length-this.dataHistory[propertyName].history); 210 | 211 | } 212 | 213 | addPropertyValueByArray (propertyName,propertyValue){ 214 | this.dataHistory[propertyName].data = propertyValue; 215 | 216 | } 217 | 218 | } 219 | 220 | 221 | 222 | //--## Table Functions and Variable 223 | 224 | 225 | export function getMatchesCountText(count) { 226 | return count === 1 ? `1 match` : `${count} matches`; 227 | } 228 | 229 | 230 | 231 | export function formatDate(date) { 232 | const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'long' }); 233 | const timeFormatter = new Intl.DateTimeFormat('en-US', { timeStyle: 'short', hour12: false }); 234 | return `${dateFormatter.format(date)}, ${timeFormatter.format(date)}`; 235 | } 236 | 237 | 238 | 239 | export function createLabelFunction(columnName) { 240 | return ({ sorted, descending }) => { 241 | const sortState = sorted ? `sorted ${descending ? 'descending' : 'ascending'}` : 'not sorted'; 242 | return `${columnName}, ${sortState}.`; 243 | }; 244 | } 245 | 246 | 247 | 248 | export const paginationLabels = { 249 | nextPageLabel: 'Next page', 250 | pageLabel: pageNumber => `Go to page ${pageNumber}`, 251 | previousPageLabel: 'Previous page', 252 | }; 253 | 254 | 255 | 256 | 257 | export const pageSizePreference = { 258 | title: 'Select page size', 259 | options: [ 260 | { value: 10, label: '10 resources' }, 261 | { value: 20, label: '20 resources' }, 262 | ], 263 | }; 264 | 265 | 266 | 267 | export function EmptyState({ title, subtitle, action }) { 268 | return ( 269 | 270 | 271 | {title} 272 | 273 | 274 | {subtitle} 275 | 276 | {action} 277 | 278 | ); 279 | } 280 | 281 | 282 | 283 | 284 | export function databaseEngineTitle(engine) { 285 | 286 | var engineTitle = ""; 287 | switch (engine) { 288 | 289 | case "docdb-elastic": 290 | case "docdb": 291 | engineTitle = "Amazon DocumentDB" 292 | break; 293 | 294 | case "dynamodb": 295 | engineTitle = "Amazon DynamoDB" 296 | break; 297 | 298 | case "elasticache:redis": 299 | case "elasticache:redis-serverless": 300 | engineTitle = "Amazon ElastiCache" 301 | break; 302 | 303 | case "memorydb:redis": 304 | case "memorydb:redis": 305 | engineTitle = "Amazon MemoryDB" 306 | break; 307 | 308 | case "aurora-mysql": 309 | case "aurora-postgresql": 310 | engineTitle = "Amazon Aurora" 311 | break; 312 | 313 | case "mysql": 314 | case "postgres": 315 | case "mariadb": 316 | case "sqlserver-se": 317 | case "sqlserver-ee": 318 | case "sqlserver-ex": 319 | case "sqlserver-web": 320 | case "oracle-ee": 321 | case "oracle-ee-cdb": 322 | case "oracle-se2": 323 | case "oracle-se2-cdb": 324 | engineTitle = "Amazon RDS" 325 | break; 326 | 327 | 328 | 329 | 330 | } 331 | 332 | return engineTitle; 333 | }; 334 | -------------------------------------------------------------------------------- /frontend/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import TopNavigation from '@cloudscape-design/components/top-navigation'; 2 | import { configuration } from '../pages/Configs'; 3 | import { applyMode, Mode } from '@cloudscape-design/global-styles'; 4 | import { databaseEngineTitle } from './Functions'; 5 | 6 | export default function App({sessionInformation,onClickMenu, onClickDisconnect, titleItem = ""}) { 7 | 8 | 9 | //-- Navigation settings 10 | const i18nStrings = { 11 | searchIconAriaLabel: 'Search', 12 | searchDismissIconAriaLabel: 'Close search', 13 | overflowMenuTriggerText: 'More', 14 | overflowMenuTitleText: 'All', 15 | overflowMenuBackIconAriaLabel: 'Back', 16 | overflowMenuDismissIconAriaLabel: 'Close menu', 17 | }; 18 | 19 | const handleClickMenu = ({detail}) => { 20 | 21 | switch (detail.id) { 22 | 23 | case 'themeDark': 24 | applyMode(Mode.Dark); 25 | localStorage.setItem("themeMode", "dark"); 26 | break; 27 | 28 | case 'themeLight': 29 | applyMode(Mode.Light); 30 | localStorage.setItem("themeMode", "light"); 31 | break; 32 | 33 | 34 | } 35 | 36 | }; 37 | 38 | //-- Navigate Profiling 39 | const profileActions = [ 40 | { type: 'button', id: 'profile', text: 'SessionID : ' + sessionInformation["session_id"]}, 41 | { id: 'version', text: 'AppVersion : ' + configuration["apps-settings"]["release"]}, 42 | { 43 | type: 'menu-dropdown', 44 | id: 'preferences', 45 | text: 'Preferences', 46 | items: [ 47 | { type: 'button', id: 'themeDark', text: 'Theme Dark' }, 48 | { type: 'button', id: 'themeLight', text: 'Theme Light'}, 49 | ] 50 | }, 51 | { 52 | type: 'menu-dropdown', 53 | id: 'support-group', 54 | text: 'Support', 55 | items: [ 56 | {id: 'documentation',text: 'Documentation'}, 57 | { id: 'feedback', text: 'Feedback'}, 58 | { id: 'support', text: 'Customer support' }, 59 | ], 60 | } 61 | ]; 62 | 63 | 64 | 65 | return ( 66 |
67 | 98 |
99 | 100 | ); 101 | } 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /frontend/src/components/HeaderApp.js: -------------------------------------------------------------------------------- 1 | import TopNavigation from '@cloudscape-design/components/top-navigation'; 2 | import { Authenticator } from "@aws-amplify/ui-react"; 3 | import { configuration } from '../pages/Configs'; 4 | import { applyMode, Mode } from '@cloudscape-design/global-styles'; 5 | 6 | export default function App() { 7 | 8 | const handleClickMenu = ({detail}) => { 9 | 10 | switch (detail.id) { 11 | 12 | case 'themeDark': 13 | applyMode(Mode.Dark); 14 | localStorage.setItem("themeMode", "dark"); 15 | break; 16 | 17 | case 'themeLight': 18 | applyMode(Mode.Light); 19 | localStorage.setItem("themeMode", "light"); 20 | break; 21 | 22 | 23 | } 24 | 25 | }; 26 | 27 | 28 | const i18nStrings = { 29 | searchIconAriaLabel: 'Search', 30 | searchDismissIconAriaLabel: 'Close search', 31 | overflowMenuTriggerText: 'More', 32 | overflowMenuTitleText: 'All', 33 | overflowMenuBackIconAriaLabel: 'Back', 34 | overflowMenuDismissIconAriaLabel: 'Close menu', 35 | }; 36 | 37 | const profileActions = [ 38 | { type: 'button', id: 'profile', text: 'AppVersion : ' + configuration["apps-settings"]["release"]}, 39 | { 40 | type: 'menu-dropdown', 41 | id: 'preferences', 42 | text: 'Preferences', 43 | items: [ 44 | { type: 'button', id: 'themeDark', text: 'Theme Dark' }, 45 | { type: 'button', id: 'themeLight', text: 'Theme Light'}, 46 | ] 47 | }, 48 | { 49 | type: 'menu-dropdown', 50 | id: 'support-group', 51 | text: 'Support', 52 | items: [ 53 | {id: 'documentation',text: 'Documentation'}, 54 | { id: 'feedback', text: 'Feedback' }, 55 | { id: 'support', text: 'Customer support' }, 56 | ], 57 | } 58 | ]; 59 | 60 | 61 | 62 | return ( 63 | 64 | 65 | 66 | {({ signOut, user }) => ( 67 |
68 | 99 | 100 |
101 | 102 | )} 103 | 104 |
105 | 106 | ); 107 | } 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /frontend/src/components/Layout.js: -------------------------------------------------------------------------------- 1 | import { useRef,useState } from 'react'; 2 | import * as React from "react"; 3 | import AppLayout from "@cloudscape-design/components/app-layout"; 4 | 5 | export const splitPanelI18nStrings: SplitPanelProps.I18nStrings = { 6 | preferencesTitle: 'Split panel preferences', 7 | preferencesPositionLabel: 'Split panel position', 8 | preferencesPositionDescription: 'Choose the default split panel position for the service.', 9 | preferencesPositionSide: 'Side', 10 | preferencesPositionBottom: 'Bottom', 11 | preferencesConfirm: 'Confirm', 12 | preferencesCancel: 'Cancel', 13 | closeButtonAriaLabel: 'Close panel', 14 | openButtonAriaLabel: 'Open panel', 15 | resizeHandleAriaLabel: 'Resize split panel', 16 | }; 17 | 18 | 19 | export default function App({pageContent,activeLink,breadCrumbs,contentType,navItems,navHeader,splitPanel,splitPanelSize,splitPanelOpen,onSplitPanelToggle}) { 20 | 21 | const appLayout = useRef(); 22 | const [splitPanelSizeInt,setsplitPanelSizeInt] = useState(splitPanelSize); 23 | 24 | const onSplitPanelResize = ({ detail: { size } }) => { 25 | setsplitPanelSizeInt(size); 26 | }; 27 | 28 | 29 | return ( 30 | <> 31 | 44 | 45 | ); 46 | } -------------------------------------------------------------------------------- /frontend/src/components/Metric01.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | 3 | const Metric = memo(({ value, title, precision, format=1, fontSizeTitle = "11px", fontSizeValue = "22px", fontColorTitle = "#2ea597", fontColorValue = "orange", postFix="" }) => { 4 | 5 | var counterValue = 0; 6 | 7 | try { 8 | switch (format) { 9 | case 1: 10 | counterValue = CustomFormatNumberRaw(value,precision); 11 | break; 12 | 13 | case 2: 14 | counterValue = CustomFormatNumberData(value,precision); 15 | break; 16 | 17 | case 3: 18 | counterValue = CustomFormatNumberRawInteger(value,0); 19 | break; 20 | 21 | case 4: 22 | counterValue = CustomFormatNumber(value,precision); 23 | break; 24 | 25 | } 26 | 27 | } 28 | catch{ 29 | console.log('error'); 30 | } 31 | 32 | 33 | 34 | function CustomFormatNumberData(value,decimalLength) { 35 | if(value == 0) return '0'; 36 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 37 | 38 | var k = 1024, 39 | sizes = ['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZT', 'YB'], 40 | i = Math.floor(Math.log(value) / Math.log(k)); 41 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 42 | } 43 | 44 | 45 | function CustomFormatNumberRaw(value,decimalLength) { 46 | if (value < 100 && decimalLength == 0 ) 47 | decimalLength=2; 48 | 49 | if (value==0) 50 | decimalLength=0; 51 | 52 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 53 | 54 | } 55 | 56 | function CustomFormatNumberRawInteger(value,decimalLength) { 57 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 58 | } 59 | 60 | 61 | function CustomFormatNumber(value,decimalLength) { 62 | if(value == 0) return '0'; 63 | if(value < 1000) return parseFloat(value).toFixed(decimalLength); 64 | 65 | var k = 1000, 66 | sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 67 | i = Math.floor(Math.log(value) / Math.log(k)); 68 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 69 | } 70 | 71 | return ( 72 |
73 | 74 | {counterValue}{postFix} 75 | 76 |
77 | 78 | {title} 79 | 80 | 81 |
82 | ) 83 | }); 84 | 85 | export default Metric 86 | 87 | -------------------------------------------------------------------------------- /frontend/src/components/Metric02.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react'; 2 | 3 | const Metric = memo(({ value, title, precision, format=1, fontSizeTitle = "11px", fontSizeValue = "22px", fontColorTitle = "#2ea597", fontColorValue = "orange"}) => { 4 | 5 | var counterValue = 0; 6 | 7 | try { 8 | switch (format) { 9 | case 1: 10 | counterValue = CustomFormatNumberRaw(value,precision); 11 | break; 12 | 13 | case 2: 14 | counterValue = CustomFormatNumberData(value,precision); 15 | break; 16 | 17 | case 3: 18 | counterValue = CustomFormatNumberRawInteger(value,0); 19 | break; 20 | 21 | } 22 | 23 | } 24 | catch{ 25 | console.log('error'); 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | function CustomFormatNumberData(value,decimalLength) { 34 | if(value == 0) return '0'; 35 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 36 | 37 | var k = 1024, 38 | sizes = ['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZT', 'YB'], 39 | i = Math.floor(Math.log(value) / Math.log(k)); 40 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 41 | } 42 | 43 | 44 | function CustomFormatNumberRaw(value,decimalLength) { 45 | if (value < 100 && decimalLength == 0 ) 46 | decimalLength=2; 47 | 48 | if (value==0) 49 | decimalLength=0; 50 | 51 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 52 | 53 | } 54 | 55 | function CustomFormatNumberRawInteger(value,decimalLength) { 56 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 57 | } 58 | 59 | return ( 60 |
61 | 62 | {counterValue} 63 | 64 |
65 | 66 | {title} 67 | 68 | 69 |
70 | ) 71 | }); 72 | 73 | export default Metric 74 | -------------------------------------------------------------------------------- /frontend/src/components/Metric03.js: -------------------------------------------------------------------------------- 1 | import {memo} from 'react' 2 | 3 | const Metric = memo(({ value, title, precision, format=1, fontSizeTitle = "11px", fontSizeValue = "22px", fontColorTitle = "#2ea597", fontColorValue = "orange" }) => { 4 | 5 | var counterValue = 0; 6 | 7 | try { 8 | switch (format) { 9 | case 1: 10 | counterValue = CustomFormatNumberRaw(value,precision); 11 | break; 12 | 13 | case 2: 14 | counterValue = CustomFormatNumberData(value,precision); 15 | break; 16 | 17 | case 3: 18 | counterValue = CustomFormatNumberRawInteger(value,0); 19 | break; 20 | 21 | } 22 | 23 | } 24 | catch{ 25 | console.log('error'); 26 | } 27 | 28 | 29 | function CustomFormatNumberData(value,decimalLength) { 30 | if(value == 0) return '0'; 31 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 32 | 33 | var k = 1024, 34 | sizes = ['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZT', 'YB'], 35 | i = Math.floor(Math.log(value) / Math.log(k)); 36 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 37 | } 38 | 39 | 40 | function CustomFormatNumberRaw(value,decimalLength) { 41 | if (value < 100 && decimalLength == 0 ) 42 | decimalLength=2; 43 | 44 | if (value==0) 45 | decimalLength=0; 46 | 47 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 48 | 49 | } 50 | 51 | function CustomFormatNumberRawInteger(value,decimalLength) { 52 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 53 | } 54 | 55 | return ( 56 |
57 | 58 | {counterValue} 59 | 60 |
61 | 62 | {title} 63 | 64 | 65 |
66 | ) 67 | }); 68 | 69 | export default Metric 70 | -------------------------------------------------------------------------------- /frontend/src/components/Metric04.js: -------------------------------------------------------------------------------- 1 | import {useState,useEffect,useRef} from 'react' 2 | import Chart from 'react-apexcharts'; 3 | 4 | function Metric({ value, title, precision, format=1, history = 20, height, width = "100%",type = "bar", fontSizeTitle = "11px", fontSizeValue = "22px", fontColorTitle = "#2ea597", fontColorValue = "orange", chartColorLine = "#F6CE55" }) { 5 | 6 | const dataChart = useRef(Array(history).fill(null)); 7 | const [dataset,setDataset] = useState({ 8 | value : 0, 9 | chart : [{ data : Array(0).fill(null) }] 10 | }); 11 | 12 | var options = { 13 | chart: { 14 | type: 'bar', 15 | sparkline: { 16 | enabled: true 17 | }, 18 | animations: { 19 | enabled: false, 20 | }, 21 | }, 22 | colors: [chartColorLine], 23 | stroke: { 24 | width: 2, 25 | }, 26 | plotOptions: { 27 | bar: { 28 | columnWidth: '80%' 29 | } 30 | }, 31 | markers: { 32 | size: 0, 33 | strokeColors: '#29313e', 34 | }, 35 | xaxis: { 36 | crosshairs: { 37 | width: 1 38 | }, 39 | }, 40 | grid: { 41 | padding: { 42 | top: 0, 43 | left: 0, 44 | right: 0, 45 | bottom: 0 46 | } 47 | }, 48 | tooltip: { 49 | theme: "dark", 50 | fixed: { 51 | enabled: false 52 | }, 53 | x: { 54 | show: false 55 | }, 56 | y: { 57 | title: { 58 | formatter: function (seriesName) { 59 | return '' 60 | } 61 | } 62 | }, 63 | marker: { 64 | show: false 65 | } 66 | } 67 | }; 68 | 69 | 70 | function updateMetrics(){ 71 | try { 72 | 73 | dataChart.current.push(value); 74 | dataChart.current = dataChart.current.slice(dataChart.current.length-history); 75 | 76 | 77 | switch (format) { 78 | case 1: 79 | setDataset({ value : CustomFormatNumberRaw(value,precision), chart : [{ data : dataChart.current }] }); 80 | break; 81 | 82 | case 2: 83 | setDataset({ value : CustomFormatNumberData(value,precision), chart : [{ data : dataChart.current }] }); 84 | break; 85 | 86 | case 3: 87 | setDataset({ value : CustomFormatNumberRawInteger(value,0), chart : [{ data : dataChart.current }] }); 88 | break; 89 | 90 | } 91 | 92 | } 93 | catch{ 94 | console.log('error'); 95 | } 96 | 97 | 98 | } 99 | 100 | // eslint-disable-next-line 101 | useEffect(() => { 102 | updateMetrics(); 103 | // eslint-disable-next-line react-hooks/exhaustive-deps 104 | }, [value]); 105 | 106 | 107 | 108 | function CustomFormatNumberData(value,decimalLength) { 109 | if(value == 0) return '0'; 110 | if(value < 1024) return parseFloat(value).toFixed(decimalLength); 111 | 112 | var k = 1024, 113 | sizes = ['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZT', 'YB'], 114 | i = Math.floor(Math.log(value) / Math.log(k)); 115 | return parseFloat((value / Math.pow(k, i)).toFixed(decimalLength)) + ' ' + sizes[i]; 116 | } 117 | 118 | 119 | function CustomFormatNumberRaw(value,decimalLength) { 120 | if (value < 100 && decimalLength == 0 ) 121 | decimalLength=2; 122 | 123 | if (value==0) 124 | decimalLength=0; 125 | 126 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 127 | 128 | } 129 | 130 | function CustomFormatNumberRawInteger(value,decimalLength) { 131 | return value.toLocaleString('en-US', {minimumFractionDigits:decimalLength, maximumFractionDigits:decimalLength}); 132 | } 133 | 134 | return ( 135 |
136 | 137 | 138 | 139 | 147 | 148 |
140 |
141 | {dataset.value} 142 |
143 |
144 | {title} 145 |
146 |
149 | 150 | 151 | 152 | 157 | 158 |
153 |
154 | 155 |
156 |
159 | 160 | 161 | 162 | 163 | 164 |
165 | 166 | 167 | 168 | ) 169 | } 170 | 171 | export default Metric 172 | -------------------------------------------------------------------------------- /frontend/src/components/ProtectedApp.js: -------------------------------------------------------------------------------- 1 | import { useAuthenticator } from "@aws-amplify/ui-react"; 2 | import { Navigate,useLocation } from "react-router-dom"; 3 | 4 | export default function Protected({ children }) { 5 | const { user } = useAuthenticator(); 6 | const location = useLocation(); 7 | 8 | if (!(user)) { 9 | return ; 10 | } 11 | 12 | sessionStorage.setItem("x-token-cognito",user.signInUserSession.accessToken.jwtToken); 13 | 14 | return children; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/components/ProtectedDb.js: -------------------------------------------------------------------------------- 1 | import { Navigate,useLocation } from "react-router-dom"; 2 | import { useSearchParams } from 'react-router-dom'; 3 | 4 | 5 | const Protected = ({ children }) => { 6 | 7 | //-- Gather Parameters 8 | const [params]=useSearchParams(); 9 | const parameter_code_id=params.get("code_id"); 10 | 11 | //-- Gather URL location 12 | const location = useLocation(); 13 | 14 | if (sessionStorage.getItem(parameter_code_id) === null ) { 15 | return ; 16 | } 17 | 18 | return children; 19 | 20 | }; 21 | 22 | export default Protected; -------------------------------------------------------------------------------- /frontend/src/components/Table01.js: -------------------------------------------------------------------------------- 1 | import {memo,useState} from 'react'; 2 | import { getMatchesCountText, paginationLabels, pageSizePreference, EmptyState } from './Functions'; 3 | 4 | import { useCollection } from '@cloudscape-design/collection-hooks'; 5 | import {CollectionPreferences,Pagination } from '@cloudscape-design/components'; 6 | import TextFilter from "@cloudscape-design/components/text-filter"; 7 | 8 | import Table from "@cloudscape-design/components/table"; 9 | import Header from "@cloudscape-design/components/header"; 10 | import Button from "@cloudscape-design/components/button"; 11 | 12 | const TableComponent = memo(({columnsTable,visibleContent, dataset, title, description = "" }) => { 13 | 14 | 15 | const [selectedItems,setSelectedItems] = useState([{ identifier: "" }]); 16 | 17 | const visibleContentPreference = { 18 | title: 'Select visible content', 19 | options: [ 20 | { 21 | label: 'Main properties', 22 | options: columnsTable.map(({ id, header }) => ({ id, label: header, editable: id !== 'id' })), 23 | }, 24 | ], 25 | }; 26 | 27 | const collectionPreferencesProps = { 28 | pageSizePreference, 29 | visibleContentPreference, 30 | cancelLabel: 'Cancel', 31 | confirmLabel: 'Confirm', 32 | title: 'Preferences', 33 | }; 34 | 35 | 36 | const [preferences, setPreferences] = useState({ pageSize: 10, visibleContent: visibleContent }); 37 | 38 | const { items, actions, filteredItemsCount, collectionProps, filterProps, paginationProps } = useCollection( 39 | dataset, 40 | { 41 | filtering: { 42 | empty: , 43 | noMatch: ( 44 | actions.setFiltering('')}>Clear filter} 47 | /> 48 | ), 49 | }, 50 | pagination: { pageSize: preferences.pageSize }, 51 | sorting: {}, 52 | selection: {}, 53 | } 54 | ); 55 | 56 | 57 | return ( 58 |
59 | 68 | {title} 69 | 70 | } 71 | columnDefinitions={columnsTable} 72 | visibleColumns={preferences.visibleContent} 73 | items={items} 74 | pagination={} 75 | filter={ 76 | 81 | } 82 | preferences={ 83 | setPreferences(detail)} 87 | /> 88 | } 89 | onSelectionChange={({ detail }) => { 90 | setSelectedItems(detail.selectedItems); 91 | } 92 | } 93 | selectedItems={selectedItems} 94 | resizableColumns 95 | stickyHeader 96 | loadingText="Loading records" 97 | /> 98 | 99 | 100 | ); 101 | }); 102 | 103 | export default TableComponent; 104 | -------------------------------------------------------------------------------- /frontend/src/components/Table02.js: -------------------------------------------------------------------------------- 1 | import {memo,useState} from 'react'; 2 | import { getMatchesCountText, paginationLabels, pageSizePreference, EmptyState } from './Functions'; 3 | 4 | import { useCollection } from '@cloudscape-design/collection-hooks'; 5 | import {CollectionPreferences,Pagination } from '@cloudscape-design/components'; 6 | import TextFilter from "@cloudscape-design/components/text-filter"; 7 | 8 | import Table from "@cloudscape-design/components/table"; 9 | import Header from "@cloudscape-design/components/header"; 10 | import Button from "@cloudscape-design/components/button"; 11 | import SpaceBetween from "@cloudscape-design/components/space-between"; 12 | 13 | 14 | const TableComponent = memo(({columnsTable,visibleContent, dataset, title, description = "", onSelectionItem = () => {}, pageSize = 10, extendedTableProperties = {}, tableActions = null }) => { 15 | 16 | 17 | const [selectedItems,setSelectedItems] = useState([{ identifier: "" }]); 18 | 19 | const visibleContentPreference = { 20 | title: 'Select visible content', 21 | options: [ 22 | { 23 | label: 'Main properties', 24 | options: columnsTable.map(({ id, header }) => ({ id, label: header, editable: id !== 'id' })), 25 | }, 26 | ], 27 | }; 28 | 29 | const collectionPreferencesProps = { 30 | pageSizePreference, 31 | visibleContentPreference, 32 | cancelLabel: 'Cancel', 33 | confirmLabel: 'Confirm', 34 | title: 'Preferences', 35 | }; 36 | 37 | 38 | const [preferences, setPreferences] = useState({ pageSize: pageSize, visibleContent: visibleContent }); 39 | 40 | const { items, actions, filteredItemsCount, collectionProps, filterProps, paginationProps } = useCollection( 41 | dataset, 42 | { 43 | filtering: { 44 | empty: , 45 | noMatch: ( 46 | actions.setFiltering('')}>Clear filter} 49 | /> 50 | ), 51 | }, 52 | pagination: { pageSize: preferences.pageSize }, 53 | sorting: {}, 54 | selection: {}, 55 | } 56 | ); 57 | 58 | function onSelectionChange(item){ 59 | onSelectionItem(item); 60 | } 61 | 62 | 63 | 64 | return ( 65 |
75 | {title} 76 | 77 | } 78 | columnDefinitions={columnsTable} 79 | visibleColumns={preferences.visibleContent} 80 | items={items} 81 | pagination={} 82 | filter={ 83 | 88 | } 89 | preferences={ 90 | setPreferences(detail)} 94 | /> 95 | } 96 | onSelectionChange={({ detail }) => { 97 | onSelectionChange(detail.selectedItems); 98 | setSelectedItems(detail.selectedItems); 99 | } 100 | } 101 | selectedItems={selectedItems} 102 | resizableColumns 103 | stickyHeader 104 | loadingText="Loading records" 105 | {...extendedTableProperties} 106 | /> 107 | 108 | ); 109 | }); 110 | 111 | export default TableComponent; 112 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import { render } from "react-dom"; 2 | import { 3 | BrowserRouter, 4 | Routes, 5 | Route, 6 | } from "react-router-dom"; 7 | 8 | //-- Libraries 9 | import '@cloudscape-design/global-styles/index.css'; 10 | import { Amplify } from "aws-amplify"; 11 | import { AmplifyProvider, Authenticator } from "@aws-amplify/ui-react"; 12 | import { StrictMode } from "react"; 13 | import Axios from "axios"; 14 | 15 | //-- Pages 16 | import Authentication from "./pages/Authentication"; 17 | import Home from "./pages/Home"; 18 | import SmRdsInstances from "./pages/Sm-rdsInstances-01"; 19 | import SmClustersElasticache from "./pages/Sm-clustersElasticache-01"; 20 | import SmClustersMemoryDB from "./pages/Sm-clustersMemorydb-01"; 21 | import SmClustersAurora from "./pages/Sm-clustersAurora-01"; 22 | import SmClustersDocumentDB from "./pages/Sm-clustersDocumentDB-01"; 23 | import Logout from "./pages/Logout"; 24 | import SmMysql01 from "./pages/Sm-mysql-01"; 25 | import SmPostgresql01 from "./pages/Sm-postgresql-01"; 26 | import SmMssql01 from "./pages/Sm-mssql-01"; 27 | import SmOracle01 from "./pages/Sm-oracle-01"; 28 | import SmElasticache01 from "./pages/Sm-elasticache-01"; 29 | import SmElasticache02 from "./pages/Sm-elasticache-02"; 30 | import SmMemoryDB01 from "./pages/Sm-memorydb-01"; 31 | import SmAuroraMysql01 from "./pages/Sm-aurora-mysql-01"; 32 | import SmAuroraPostgresql01 from "./pages/Sm-aurora-postgresql-01"; 33 | import SmAuroraPostgresql02 from "./pages/Sm-aurora-postgresql-02"; 34 | import SmDocumentDB01 from "./pages/Sm-documentdb-01"; 35 | import SmDocumentDB02 from "./pages/Sm-documentdb-02"; 36 | import SmTablesDynamoDB01 from "./pages/Sm-tablesDynamoDB-01"; 37 | import SmDynamoDB01 from "./pages/Sm-dynamodb-01"; 38 | import SmApplicationUpdate from "./pages/Sm-appUpdate"; 39 | import SmEngineConnections from "./pages/Sm-engineConnections"; 40 | 41 | 42 | 43 | //-- Components 44 | import ProtectedDb from "./components/ProtectedDb"; 45 | import ProtectedApp from "./components/ProtectedApp"; 46 | 47 | import { applyMode, Mode } from '@cloudscape-design/global-styles'; 48 | 49 | if (localStorage.getItem("themeMode") === null ){ 50 | localStorage.setItem("themeMode", "light"); 51 | } 52 | 53 | if (localStorage.getItem("themeMode") == "dark") 54 | applyMode(Mode.Dark); 55 | else 56 | applyMode(Mode.Light); 57 | 58 | 59 | Axios.get(`/aws-exports.json`,).then((data)=>{ 60 | 61 | var configData = data.data; 62 | Amplify.configure({ 63 | Auth: { 64 | region: configData.aws_region, 65 | userPoolId: configData.aws_cognito_user_pool_id, 66 | userPoolWebClientId: configData.aws_cognito_user_pool_web_client_id, 67 | }, 68 | }); 69 | 70 | const rootElement = document.getElementById("root"); 71 | render( 72 | 73 | 74 | 75 | 76 | 77 | } /> 78 | } /> 79 | } /> 80 | } /> 81 | } /> 82 | } /> 83 | } /> 84 | } /> 85 | } /> 86 | } /> 87 | } /> 88 | } /> 89 | } /> 90 | } /> 91 | } /> 92 | } /> 93 | } /> 94 | } /> 95 | } /> 96 | } /> 97 | } /> 98 | } /> 99 | } /> 100 | } /> 101 | 102 | 103 | 104 | 105 | , 106 | rootElement 107 | ); 108 | 109 | }) 110 | .catch((err) => { 111 | console.log('API Call error : ./aws-exports.json' ); 112 | console.log(err) 113 | }); 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /frontend/src/pages/Authentication.js: -------------------------------------------------------------------------------- 1 | import { 2 | Authenticator, 3 | Flex, 4 | Grid, 5 | useTheme, 6 | View, 7 | Heading, 8 | Text 9 | } from "@aws-amplify/ui-react"; 10 | 11 | import { configuration } from '../pages/Configs'; 12 | 13 | import { Navigate,useLocation } from "react-router-dom"; 14 | 15 | export default function Auth({children}) { 16 | 17 | const location = useLocation(); 18 | const from = location.state?.from || "/"; 19 | 20 | const { tokens } = useTheme(); 21 | const components = { 22 | Header, 23 | SignIn: { 24 | Header: SignInHeader 25 | }, 26 | Footer 27 | }; 28 | 29 | 30 | function Header() { 31 | const { tokens } = useTheme(); 32 | return ( 33 | 34 | 35 | 36 | ); 37 | } 38 | 39 | 40 | function Footer() { 41 | const { tokens } = useTheme(); 42 | 43 | return ( 44 | 45 | 46 | 47 | ); 48 | } 49 | 50 | 51 | function SignInHeader() { 52 | const { tokens } = useTheme(); 53 | 54 | return ( 55 | 56 | Sign in to {configuration['apps-settings']['application-title']} Console 57 | 58 | ); 59 | } 60 | 61 | 62 | return ( 63 | 64 | 68 | 69 | {({ signOut, user }) => ( 70 |
71 | ; 72 |
73 | )} 74 |
75 |
76 | 79 | 80 |
81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /frontend/src/pages/Configs.js: -------------------------------------------------------------------------------- 1 | 2 | export const configuration = 3 | { 4 | "apps-settings": { 5 | "refresh-interval": 10*1000, 6 | "refresh-interval-rds": 5*1000, 7 | "refresh-interval-clw": 20*1000, 8 | "refresh-interval-elastic": 5*1000, 9 | "refresh-interval-documentdb-metrics": 5*1000, 10 | "refresh-interval-documentdb-sessions": 5*1000, 11 | "refresh-interval-aurora-pgs-sessions": 10*1000, 12 | "refresh-interval-dynamodb": 5*1000, 13 | "refresh-interval-update": 5*1000, 14 | "refresh-interval-aurora-limitless": 10*1000, 15 | "items-per-page-aurora": 16, 16 | "items-per-page-documentdb": 64, 17 | "api_url": "", 18 | "release" : "0.1.3", 19 | "release-enforcement" : false, 20 | "application-title": "DBTop Monitoring", 21 | "version-code-url" : "https://version.code.ds.wwcs.aws.dev/", 22 | "items-per-page": 10 23 | }, 24 | "colors": { 25 | "fonts" : { 26 | "metric102" : "#4595dd", 27 | "metric101" : "#e59400", 28 | "metric100" : "", 29 | }, 30 | "lines" : { 31 | "separator100" : "#737c85", 32 | "separator101" : "#9e9b9a" 33 | } 34 | } 35 | }; 36 | 37 | export const SideMainLayoutHeader = { text: 'Database Services', href: '#/' }; 38 | 39 | export const SideMainLayoutMenu = [ 40 | { type: "link", text: "Home", href: "/" }, 41 | { 42 | text: 'Relational Engines', 43 | type: 'section', 44 | defaultExpanded: true, 45 | items: [ 46 | { type: 'link', text: 'RDS Instances', href: '/rds/instances/' }, 47 | { type: 'link', text: 'Aurora Clusters', href: '/clusters/aurora/'}, 48 | ], 49 | }, 50 | { type: "divider" }, 51 | { 52 | text: 'NON-Relational Engines', 53 | type: 'section', 54 | defaultExpanded: true, 55 | items: [ 56 | { type: 'link', text: 'ElastiCache Clusters', href: '/clusters/elasticache/' }, 57 | { type: 'link', text: 'MemoryDB Clusters', href: '/clusters/memorydb/'}, 58 | { type: 'link', text: 'DocumentDB Clusters', href: '/clusters/documentdb/'}, 59 | { type: 'link', text: 'DynamoDB Tables', href: '/tables/dynamodb/'}, 60 | ], 61 | }, 62 | { type: "divider" }, 63 | { type: 'link', text: 'Application Update', href: '/update/'}, 64 | { type: 'link', text: 'Engine Connections', href: '/connections/'}, 65 | { 66 | type: "link", 67 | text: "Documentation", 68 | href: "https://github.com/aws-samples/db-top-monitoring", 69 | external: true, 70 | externalIconAriaLabel: "Opens in a new tab" 71 | } 72 | ]; 73 | 74 | 75 | export const breadCrumbs = [{text: 'Service',href: '#',},{text: 'Resource search',href: '#',},]; 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /frontend/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import {useState,useEffect} from 'react' 2 | 3 | import { SideMainLayoutHeader,SideMainLayoutMenu, breadCrumbs } from './Configs'; 4 | 5 | 6 | //import ContentLayout from '@cloudscape-design/components/content-layout'; 7 | 8 | import CustomHeader from "../components/HeaderApp"; 9 | import AppLayout from "@cloudscape-design/components/app-layout"; 10 | import SideNavigation from '@cloudscape-design/components/side-navigation'; 11 | import ContentLayout from '@cloudscape-design/components/content-layout'; 12 | import { configuration } from './Configs'; 13 | import { applicationVersionUpdate } from '../components/Functions'; 14 | 15 | import Flashbar from "@cloudscape-design/components/flashbar"; 16 | import Button from "@cloudscape-design/components/button"; 17 | import Container from "@cloudscape-design/components/container"; 18 | import Header from "@cloudscape-design/components/header"; 19 | import Box from "@cloudscape-design/components/box"; 20 | import ColumnLayout from "@cloudscape-design/components/column-layout"; 21 | import Badge from "@cloudscape-design/components/badge"; 22 | 23 | import '@aws-amplify/ui-react/styles.css'; 24 | 25 | export const splitPanelI18nStrings: SplitPanelProps.I18nStrings = { 26 | preferencesTitle: 'Split panel preferences', 27 | preferencesPositionLabel: 'Split panel position', 28 | preferencesPositionDescription: 'Choose the default split panel position for the service.', 29 | preferencesPositionSide: 'Side', 30 | preferencesPositionBottom: 'Bottom', 31 | preferencesConfirm: 'Confirm', 32 | preferencesCancel: 'Cancel', 33 | closeButtonAriaLabel: 'Close panel', 34 | openButtonAriaLabel: 'Open panel', 35 | resizeHandleAriaLabel: 'Resize split panel', 36 | }; 37 | 38 | 39 | function Home() { 40 | 41 | //-- Application Version 42 | const [versionMessage, setVersionMessage] = useState([]); 43 | 44 | 45 | //-- Call API to App Version 46 | async function gatherVersion (){ 47 | 48 | //-- Application Update 49 | var appVersionObject = await applicationVersionUpdate({ codeId : "dbtop", moduleId: "global"} ); 50 | 51 | if (appVersionObject.release > configuration["apps-settings"]["release"] ){ 52 | setVersionMessage([ 53 | { 54 | type: "info", 55 | content: "New Application version is available, new features and modules will improve monitoring capabilities and user experience.", 56 | dismissible: true, 57 | dismissLabel: "Dismiss message", 58 | onDismiss: () => setVersionMessage([]), 59 | id: "message_1" 60 | } 61 | ]); 62 | 63 | } 64 | 65 | } 66 | 67 | 68 | useEffect(() => { 69 | gatherVersion(); 70 | // eslint-disable-next-line react-hooks/exhaustive-deps 71 | }, []); 72 | 73 | 74 | return ( 75 | 76 |
77 | 78 | } 83 | contentType="default" 84 | toolsHide={true} 85 | content={ 86 | <> 87 | 88 | 93 |
94 | Welcome to {configuration["apps-settings"]["application-title"]} Solution 95 |
96 |
97 | 98 | Gain Monitoring Insight and Take Action on AWS Database Resources. 99 | 100 |
101 | 102 | View performance data for AWS Database instances and clusters, so you can quickly identify and act on any issues that might impact database resources. 103 | 104 |
105 | 106 | 107 | 108 | } 109 | 110 | > 111 | 112 | 113 | 114 | } 115 | > 116 | 117 |
118 | 119 | 120 |
121 | 124 | How it works? 125 | 126 | 127 | } 128 | > 129 |
130 | 1 Connect to your AWS Database resorces. 131 |
132 |
133 | 2 Gather realtime performance database metrics from engine itself. 134 |
135 |
136 | 3 Extract performance from AWS Cloudwatch metrics and Enhanced Monitoring. 137 |
138 |
139 | 4 Consolidate all information into centralized dashboard. 140 |
141 |
142 | 143 |
144 | 145 |
146 | 149 | Getting Started 150 | 151 | 152 | } 153 | > 154 |
155 | 156 | Start connecting to your AWS RDS instances or Amazon Aurora, ElastiCache, MemoryDB, DocumentDB clusters. 157 | 158 |
159 | 160 |
161 |
162 |
163 |
164 | 165 |
166 | 167 | 168 |
169 |
170 | 173 | Use cases 174 | 175 | 176 | } 177 | > 178 | 179 |
180 |
181 | Monitor instance performance 182 |
183 | 184 | Visualize performance data on realtime, and correlate data to understand and resolve the root cause of performance issues in your database resources. 185 | 186 |
187 |
188 |
189 | Perform root cause analysis 190 |
191 | 192 | Analyze database and operating system metrics to speed up debugging and reduce overall mean time to resolution. 193 | 194 |
195 |
196 |
197 | Optimize resources proactively 198 |
199 | 200 | Identify top consumer sessions, gather database statements and resource usages. 201 | 202 |
203 | 204 |
205 | 206 |
207 | 208 | 209 |
210 |
211 | 212 | 213 | 214 | } 215 | /> 216 | 217 |
218 | ); 219 | } 220 | 221 | export default Home; 222 | -------------------------------------------------------------------------------- /frontend/src/pages/Logout.js: -------------------------------------------------------------------------------- 1 | import { useAuthenticator } from '@aws-amplify/ui-react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | 4 | const App = () => { 5 | const navigate = useNavigate(); 6 | 7 | const { signOut } = useAuthenticator((context) => [context.user]); 8 | signOut(); 9 | navigate('/'); 10 | 11 | return ( 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /frontend/src/pages/Sm-appUpdate.js: -------------------------------------------------------------------------------- 1 | import { useState,useEffect } from 'react' 2 | import Axios from 'axios' 3 | import { configuration, SideMainLayoutHeader,SideMainLayoutMenu } from './Configs'; 4 | 5 | import { applicationVersionUpdate, gatherLocalVersion } from '../components/Functions'; 6 | import { createLabelFunction } from '../components/Functions'; 7 | 8 | import SideNavigation from '@cloudscape-design/components/side-navigation'; 9 | import AppLayout from '@cloudscape-design/components/app-layout'; 10 | 11 | import Flashbar from "@cloudscape-design/components/flashbar"; 12 | import Container from "@cloudscape-design/components/container"; 13 | import Spinner from "@cloudscape-design/components/spinner"; 14 | 15 | import SpaceBetween from "@cloudscape-design/components/space-between"; 16 | import Button from "@cloudscape-design/components/button"; 17 | import CustomHeader from "../components/HeaderApp"; 18 | import CustomTable02 from "../components/Table02"; 19 | 20 | import Header from "@cloudscape-design/components/header"; 21 | import '@aws-amplify/ui-react/styles.css'; 22 | 23 | 24 | function Application() { 25 | 26 | //-- Application Version 27 | const [versionMessage, setVersionMessage] = useState([]); 28 | const [messages, setMessages] = useState([]); 29 | const [updateStatus, setUpdateStatus] = useState({ status : "", releaseVersion : "-", releaseDate : "-" }); 30 | 31 | //-- Add Header Cognito Token 32 | Axios.defaults.headers.common['x-csrf-token'] = sessionStorage.getItem("x-csrf-token"); 33 | Axios.defaults.headers.common['x-token-cognito'] = sessionStorage.getItem("x-token-cognito"); 34 | Axios.defaults.withCredentials = true; 35 | 36 | 37 | //-- Table Messages 38 | const columnsTable = [ 39 | {id: 'timestamp', header: 'Timestamp',cell: item => item['timestamp'],ariaLabel: createLabelFunction('timestamp'),sortingField: 'timestamp', width: 230,}, 40 | {id: 'message', header: 'Messages',cell: item => item['message'],ariaLabel: createLabelFunction('message'),sortingField: 'message',} 41 | ]; 42 | 43 | const visibleContent = ['timestamp','message']; 44 | 45 | 46 | //-- Gather Import Process 47 | async function gatherApplicationUpdateStatus (){ 48 | 49 | var version = await gatherLocalVersion(); 50 | 51 | console.log(version); 52 | 53 | try { 54 | 55 | var api_url = configuration["apps-settings"]["api_url"]; 56 | var params = {}; 57 | Axios.get(`${api_url}/api/aws/application/update/status`,{ 58 | params: params, 59 | }).then((data)=>{ 60 | setMessages(data.data.messages); 61 | setUpdateStatus({ status : data.data.status, releaseVersion : version['release'], releaseDate : version['date'] } ); 62 | }) 63 | .catch((err) => { 64 | console.log('Timeout API Call : /api/aws/application/update/status' ); 65 | console.log(err); 66 | }); 67 | 68 | } 69 | catch{ 70 | 71 | console.log('Timeout API error : /api/aws/application/update/status'); 72 | 73 | } 74 | 75 | } 76 | 77 | function onClickUpdate(){ 78 | 79 | try { 80 | 81 | setMessages([]); 82 | var api_url = configuration["apps-settings"]["api_url"]; 83 | var params = {}; 84 | Axios.get(`${api_url}/api/aws/application/update/start`,{ 85 | params: params, 86 | }).then((data)=>{ 87 | 88 | }) 89 | .catch((err) => { 90 | console.log('Timeout API Call : /api/aws/application/update/start' ); 91 | console.log(err); 92 | }); 93 | 94 | } 95 | catch{ 96 | 97 | console.log('Timeout API error : /api/aws/application/update/start'); 98 | 99 | } 100 | 101 | 102 | } 103 | 104 | //-- Function Gather App Version 105 | async function gatherVersion (){ 106 | 107 | //-- Application Update 108 | var appVersionObject = await applicationVersionUpdate({ codeId : "dbwcmp", moduleId: "elastic-m1"} ); 109 | 110 | if (appVersionObject.release > configuration["apps-settings"]["release"] ){ 111 | setVersionMessage([ 112 | { 113 | type: "info", 114 | content: "New Application version is available, new features and modules will improve monitoring capabilities and user experience.", 115 | dismissible: true, 116 | dismissLabel: "Dismiss message", 117 | onDismiss: () => setVersionMessage([]), 118 | id: "message_1" 119 | } 120 | ]); 121 | 122 | } 123 | 124 | } 125 | 126 | useEffect(() => { 127 | gatherVersion(); 128 | //gatherApplicationUpdateStatus(); 129 | const id = setInterval(gatherApplicationUpdateStatus, configuration["apps-settings"]["refresh-interval-update"]); 130 | return () => clearInterval(id); 131 | // eslint-disable-next-line react-hooks/exhaustive-deps 132 | }, []); 133 | 134 | 135 | 136 | 137 | 138 | return ( 139 |
140 | 141 | } 145 | contentType="default" 146 | content={ 147 | 148 |
149 | 150 | 151 |
152 | 153 | { ( updateStatus['status'] == 'in-progress' ) && 154 | 155 | } 156 | Application Update 157 | 158 | } 159 | > 160 | { 168 | } 169 | } 170 | extendedTableProperties = { 171 | { variant : "borderless" } 172 | 173 | } 174 | tableActions = { 175 | 179 | 180 | 181 | 182 | 183 | } 184 | 185 | /> 186 | 187 |
188 | } 189 | disableContentHeaderOverlap={true} 190 | headerSelector="#h" 191 | /> 192 | 193 |
194 | ); 195 | } 196 | 197 | export default Application; 198 | 199 | -------------------------------------------------------------------------------- /frontend/src/pages/Sm-engineConnections.js: -------------------------------------------------------------------------------- 1 | import { useState,useEffect } from 'react' 2 | import Axios from 'axios' 3 | import { configuration, SideMainLayoutHeader,SideMainLayoutMenu } from './Configs'; 4 | 5 | import { gatherLocalVersion } from '../components/Functions'; 6 | import { createLabelFunction } from '../components/Functions'; 7 | 8 | import SideNavigation from '@cloudscape-design/components/side-navigation'; 9 | import AppLayout from '@cloudscape-design/components/app-layout'; 10 | 11 | import Container from "@cloudscape-design/components/container"; 12 | import Spinner from "@cloudscape-design/components/spinner"; 13 | 14 | import SpaceBetween from "@cloudscape-design/components/space-between"; 15 | import Button from "@cloudscape-design/components/button"; 16 | import CustomHeader from "../components/HeaderApp"; 17 | import CustomTable02 from "../components/Table02"; 18 | 19 | import Header from "@cloudscape-design/components/header"; 20 | import '@aws-amplify/ui-react/styles.css'; 21 | 22 | 23 | function Application() { 24 | 25 | //-- Application Version 26 | 27 | const [engineConenctions, setEngineConnections] = useState([]); 28 | const [selectedItems,setSelectedItems] = useState({ engineId : "", type : "" }); 29 | 30 | 31 | //-- Add Header Cognito Token 32 | Axios.defaults.headers.common['x-csrf-token'] = sessionStorage.getItem("x-csrf-token"); 33 | Axios.defaults.headers.common['x-token-cognito'] = sessionStorage.getItem("x-token-cognito"); 34 | Axios.defaults.withCredentials = true; 35 | 36 | 37 | //-- Table Messages 38 | const columnsTable = [ 39 | {id: 'connectionId', header: 'ConnectionId',cell: item => item['connectionId'],ariaLabel: createLabelFunction('connectionId'),sortingField: 'connectionId'}, 40 | {id: 'engineId', header: 'EngineId',cell: item => item['engineId'],ariaLabel: createLabelFunction('engineId'),sortingField: 'engineId'}, 41 | {id: 'type', header: 'Type',cell: item => item['type'],ariaLabel: createLabelFunction('type'),sortingField: 'type'}, 42 | {id: 'creationTime', header: 'CreationTime',cell: item => item['creationTime'],ariaLabel: createLabelFunction('creationTime'),sortingField: 'creationTime',} 43 | ]; 44 | 45 | const visibleContent = ['connectionId','engineId','creationTime']; 46 | 47 | 48 | //-- Gather Connections 49 | async function gatherConnections (){ 50 | 51 | try { 52 | 53 | var api_url = configuration["apps-settings"]["api_url"]; 54 | var params = {}; 55 | Axios.get(`${api_url}/api/aws/engines/connections/list`,{ 56 | params: params, 57 | }).then((data)=>{ 58 | console.log(data.data); 59 | setEngineConnections(data.data.engineConnections); 60 | 61 | }) 62 | .catch((err) => { 63 | console.log('Timeout API Call : /api/aws/engines/connections/list' ); 64 | console.log(err); 65 | }); 66 | 67 | } 68 | catch{ 69 | 70 | console.log('Timeout API error : /api/aws/engines/connections/list'); 71 | 72 | } 73 | 74 | } 75 | 76 | function onClickTerminate(){ 77 | 78 | try { 79 | 80 | var api_url = configuration["apps-settings"]["api_url"]; 81 | var params = { engineId : selectedItems['engineId'], type : selectedItems['type'] }; 82 | Axios.get(`${api_url}/api/aws/engines/connections/terminate`,{ 83 | params: params, 84 | }).then((data)=>{ 85 | 86 | gatherConnections(); 87 | }) 88 | .catch((err) => { 89 | console.log('Timeout API Call : /api/aws/engines/connections/terminate' ); 90 | console.log(err); 91 | }); 92 | 93 | } 94 | catch{ 95 | 96 | console.log('Timeout API error : /api/aws/engines/connections/terminate'); 97 | 98 | } 99 | 100 | 101 | } 102 | 103 | 104 | 105 | useEffect(() => { 106 | gatherConnections(); 107 | //const id = setInterval(gatherConnections, configuration["apps-settings"]["refresh-interval-update"]); 108 | //return () => clearInterval(id); 109 | // eslint-disable-next-line react-hooks/exhaustive-deps 110 | }, []); 111 | 112 | 113 | return ( 114 |
115 | 116 | } 120 | contentType="default" 121 | content={ 122 | 123 |
124 | 125 | 141 | 142 | 143 | 144 | } 145 | onSelectionItem={( item ) => { 146 | setSelectedItems(item[0]); 147 | } 148 | } 149 | /> 150 | 151 |
152 | } 153 | disableContentHeaderOverlap={true} 154 | headerSelector="#h" 155 | /> 156 | 157 |
158 | ); 159 | } 160 | 161 | export default Application; 162 | 163 | -------------------------------------------------------------------------------- /frontend/src/styles/css/animation.css: -------------------------------------------------------------------------------- 1 | @keyframes placeholderAnimation { 2 | 0% { 3 | background-position:100% 100%; /*OR bottom right*/ 4 | } 5 | 100% { 6 | background-position:0 0; /*OR top left*/ 7 | } 8 | } -------------------------------------------------------------------------------- /frontend/src/styles/css/base.css: -------------------------------------------------------------------------------- 1 | @use '~@cloudscape-design/design-tokens' as cs; 2 | 3 | body { 4 | background: cs.$color-background-layout-main; 5 | position: relative; 6 | } 7 | 8 | .custom-main-header { 9 | display: block; 10 | position: sticky; 11 | top: 0; 12 | left: 0; 13 | right: 0; 14 | z-index: 1000; 15 | margin: 0; 16 | background-color: #0f1b2a; 17 | font-family: cs.$font-family-base; 18 | } 19 | 20 | ul.menu-list { 21 | display: flex; 22 | align-items: center; 23 | height: 40px; 24 | margin: 0; 25 | padding: 0 40px; 26 | list-style: none; 27 | font-size: 14px; 28 | 29 | & > li { 30 | padding: 0; 31 | margin: 0; 32 | margin-right: 8px; 33 | 34 | > a { 35 | padding: 0 6px; 36 | } 37 | 38 | a, 39 | div, 40 | button, 41 | input, 42 | label { 43 | float: left; 44 | color: cs.$color-text-interactive-default; 45 | line-height: 16px; 46 | } 47 | 48 | #visual-refresh-toggle { 49 | margin-right: 5px; 50 | margin-top: 1px; 51 | } 52 | 53 | a, 54 | a:hover { 55 | cursor: pointer; 56 | text-decoration: none; 57 | } 58 | 59 | &.title { 60 | font-weight: bold; 61 | } 62 | } 63 | 64 | @media only screen and (max-width: 493px) { 65 | padding: 4px 20px; 66 | flex-wrap: wrap; 67 | height: fit-content; 68 | 69 | .title { 70 | flex: 1 1 100%; 71 | margin-bottom: 8px; 72 | } 73 | 74 | li { 75 | width: min-content; 76 | 77 | button, 78 | a { 79 | text-align: left; 80 | } 81 | 82 | a { 83 | padding: 0; 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /frontend/src/styles/css/default.css: -------------------------------------------------------------------------------- 1 | #tableflat { 2 | font-family: Arial, Helvetica, sans-serif; 3 | border-collapse: collapse; 4 | width: 100%; 5 | padding: 1em 6 | } 7 | 8 | #tableflat td, #tableflat th { 9 | border: 1px solid #eaeded; 10 | padding: 8px; 11 | } 12 | 13 | 14 | 15 | #tableflat th { 16 | padding-top: 12px; 17 | padding-bottom: 12px; 18 | text-align: left; 19 | background-color: #fafafa; 20 | color: black; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/styles/css/top-navigation.css: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | #h { 5 | z-index: 1002; 6 | } 7 | 8 | .menu-list { 9 | border-bottom: 1px solid #414750; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /frontend/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DBTop Monitoring Solution 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /frontend/www/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/frontend/www/loading.gif -------------------------------------------------------------------------------- /images/dbtop.aurora.limitless.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/dbtop.aurora.limitless.gif -------------------------------------------------------------------------------- /images/dbtop.aurora.limitless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/dbtop.aurora.limitless.png -------------------------------------------------------------------------------- /images/image01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/image01.png -------------------------------------------------------------------------------- /images/image02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/image02.png -------------------------------------------------------------------------------- /images/image03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/image03.png -------------------------------------------------------------------------------- /images/image04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/image04.png -------------------------------------------------------------------------------- /images/image05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/image05.png -------------------------------------------------------------------------------- /images/image06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/image06.png -------------------------------------------------------------------------------- /images/vid02.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/db-top-monitoring/2078056c79bde38f8c57abdd00d8488b953cbaac/images/vid02.mp4 -------------------------------------------------------------------------------- /server/aws-node-types.json: -------------------------------------------------------------------------------- 1 | { 2 | "cache.a1.medium":500000000, 3 | "cache.a1.large":750000000, 4 | "cache.a1.xlarge":1250000000, 5 | "cache.a1.2xlarge":2500000000, 6 | "cache.a1.4xlarge":5000000000, 7 | "cache.a1.metal":5000000000, 8 | "cache.m5.large":750000000, 9 | "cache.m5.xlarge":1250000000, 10 | "cache.m5.2xlarge":2500000000, 11 | "cache.m5.4xlarge":5000000000, 12 | "cache.m5a.large":750000000, 13 | "cache.m5a.xlarge":1250000000, 14 | "cache.m5a.2xlarge":2500000000, 15 | "cache.m5a.4xlarge":5000000000, 16 | "cache.m5a.8xlarge":7500000000, 17 | "cache.m5ad.large":750000000, 18 | "cache.m5ad.xlarge":1250000000, 19 | "cache.m5ad.2xlarge":2500000000, 20 | "cache.m5ad.4xlarge":5000000000, 21 | "cache.m5ad.8xlarge":7500000000, 22 | "cache.m5d.large":750000000, 23 | "cache.m5d.xlarge":1250000000, 24 | "cache.m5d.2xlarge":2500000000, 25 | "cache.m5d.4xlarge":5000000000, 26 | "cache.m5dn.large":2100000000, 27 | "cache.m5dn.xlarge":4100000000, 28 | "cache.m5dn.2xlarge":8125000000, 29 | "cache.m5dn.4xlarge":16250000000, 30 | "cache.m5n.large":2100000000, 31 | "cache.m5n.xlarge":4100000000, 32 | "cache.m5n.2xlarge":8125000000, 33 | "cache.m5n.4xlarge":16250000000, 34 | "cache.m5zn.large":3000000000, 35 | "cache.m5zn.xlarge":5000000000, 36 | "cache.m5zn.2xlarge":10000000000, 37 | "cache.m5zn.3xlarge":15000000000, 38 | "cache.m6a.large":781000000, 39 | "cache.m6a.xlarge":1562000000, 40 | "cache.m6a.2xlarge":3125000000, 41 | "cache.m6a.4xlarge":6250000000, 42 | "cache.m6g.medium":500000000, 43 | "cache.m6g.large":750000000, 44 | "cache.m6g.xlarge":1250000000, 45 | "cache.m6g.2xlarge":2500000000, 46 | "cache.m6g.4xlarge":5000000000, 47 | "cache.m6gd.medium":500000000, 48 | "cache.m6gd.large":750000000, 49 | "cache.m6gd.xlarge":1250000000, 50 | "cache.m6gd.2xlarge":2500000000, 51 | "cache.m6gd.4xlarge":5000000000, 52 | "cache.m6i.large":781000000, 53 | "cache.m6i.xlarge":1562000000, 54 | "cache.m6i.2xlarge":3125000000, 55 | "cache.m6i.4xlarge":6250000000, 56 | "cache.m6id.large":781000000, 57 | "cache.m6id.xlarge":1562000000, 58 | "cache.m6id.2xlarge":3125000000, 59 | "cache.m6id.4xlarge":6250000000, 60 | "cache.m6idn.large":3125000000, 61 | "cache.m6idn.xlarge":6250000000, 62 | "cache.m6idn.2xlarge":12500000000, 63 | "cache.m6idn.4xlarge":25000000000, 64 | "cache.m6in.large":3125000000, 65 | "cache.m6in.xlarge":6250000000, 66 | "cache.m6in.2xlarge":12500000000, 67 | "cache.m6in.4xlarge":25000000000, 68 | "cache.m7a.medium":390000000, 69 | "cache.m7a.large":781000000, 70 | "cache.m7a.xlarge":1562000000, 71 | "cache.m7a.2xlarge":3125000000, 72 | "cache.m7a.4xlarge":6250000000, 73 | "cache.m7g.medium":520000000, 74 | "cache.m7g.large":937000000, 75 | "cache.m7g.xlarge":1876000000, 76 | "cache.m7g.2xlarge":3750000000, 77 | "cache.m7g.4xlarge":7500000000, 78 | "cache.m7gd.medium":520000000, 79 | "cache.m7gd.large":937000000, 80 | "cache.m7gd.xlarge":1876000000, 81 | "cache.m7gd.2xlarge":3750000000, 82 | "cache.m7gd.4xlarge":7500000000, 83 | "cache.m7i.large":781000000, 84 | "cache.m7i.xlarge":1562000000, 85 | "cache.m7i.2xlarge":3125000000, 86 | "cache.m7i.4xlarge":6250000000, 87 | "cache.m7i-flex.large":390000000, 88 | "cache.m7i-flex.xlarge":781000000, 89 | "cache.m7i-flex.2xlarge":1562000000, 90 | "cache.m7i-flex.4xlarge":3125000000, 91 | "cache.m7i-flex.8xlarge":6250000000, 92 | "cache.t3.nano":32000000, 93 | "cache.t3.micro":64000000, 94 | "cache.t3.small":128000000, 95 | "cache.t3.medium":256000000, 96 | "cache.t3.large":512000000, 97 | "cache.t3.xlarge":1024000000, 98 | "cache.t3.2xlarge":2048000000, 99 | "cache.t3a.nano":32000000, 100 | "cache.t3a.micro":64000000, 101 | "cache.t3a.small":128000000, 102 | "cache.t3a.medium":256000000, 103 | "cache.t3a.large":512000000, 104 | "cache.t3a.xlarge":1024000000, 105 | "cache.t3a.2xlarge":2048000000, 106 | "cache.t4g.nano":32000000, 107 | "cache.t4g.micro":64000000, 108 | "cache.t4g.small":128000000, 109 | "cache.t4g.medium":256000000, 110 | "cache.t4g.large":512000000, 111 | "cache.t4g.xlarge":1024000000, 112 | "cache.t4g.2xlarge":2048000000, 113 | "db.a1.medium":500000000, 114 | "db.a1.large":750000000, 115 | "db.a1.xlarge":1250000000, 116 | "db.a1.2xlarge":2500000000, 117 | "db.a1.4xlarge":5000000000, 118 | "db.a1.metal":5000000000, 119 | "db.m5.large":750000000, 120 | "db.m5.xlarge":1250000000, 121 | "db.m5.2xlarge":2500000000, 122 | "db.m5.4xlarge":5000000000, 123 | "db.m5a.large":750000000, 124 | "db.m5a.xlarge":1250000000, 125 | "db.m5a.2xlarge":2500000000, 126 | "db.m5a.4xlarge":5000000000, 127 | "db.m5a.8xlarge":7500000000, 128 | "db.m5ad.large":750000000, 129 | "db.m5ad.xlarge":1250000000, 130 | "db.m5ad.2xlarge":2500000000, 131 | "db.m5ad.4xlarge":5000000000, 132 | "db.m5ad.8xlarge":7500000000, 133 | "db.m5d.large":750000000, 134 | "db.m5d.xlarge":1250000000, 135 | "db.m5d.2xlarge":2500000000, 136 | "db.m5d.4xlarge":5000000000, 137 | "db.m5dn.large":2100000000, 138 | "db.m5dn.xlarge":4100000000, 139 | "db.m5dn.2xlarge":8125000000, 140 | "db.m5dn.4xlarge":16250000000, 141 | "db.m5n.large":2100000000, 142 | "db.m5n.xlarge":4100000000, 143 | "db.m5n.2xlarge":8125000000, 144 | "db.m5n.4xlarge":16250000000, 145 | "db.m5zn.large":3000000000, 146 | "db.m5zn.xlarge":5000000000, 147 | "db.m5zn.2xlarge":10000000000, 148 | "db.m5zn.3xlarge":15000000000, 149 | "db.m6a.large":781000000, 150 | "db.m6a.xlarge":1562000000, 151 | "db.m6a.2xlarge":3125000000, 152 | "db.m6a.4xlarge":6250000000, 153 | "db.m6g.medium":500000000, 154 | "db.m6g.large":750000000, 155 | "db.m6g.xlarge":1250000000, 156 | "db.m6g.2xlarge":2500000000, 157 | "db.m6g.4xlarge":5000000000, 158 | "db.m6gd.medium":500000000, 159 | "db.m6gd.large":750000000, 160 | "db.m6gd.xlarge":1250000000, 161 | "db.m6gd.2xlarge":2500000000, 162 | "db.m6gd.4xlarge":5000000000, 163 | "db.m6i.large":781000000, 164 | "db.m6i.xlarge":1562000000, 165 | "db.m6i.2xlarge":3125000000, 166 | "db.m6i.4xlarge":6250000000, 167 | "db.m6id.large":781000000, 168 | "db.m6id.xlarge":1562000000, 169 | "db.m6id.2xlarge":3125000000, 170 | "db.m6id.4xlarge":6250000000, 171 | "db.m6idn.large":3125000000, 172 | "db.m6idn.xlarge":6250000000, 173 | "db.m6idn.2xlarge":12500000000, 174 | "db.m6idn.4xlarge":25000000000, 175 | "db.m6in.large":3125000000, 176 | "db.m6in.xlarge":6250000000, 177 | "db.m6in.2xlarge":12500000000, 178 | "db.m6in.4xlarge":25000000000, 179 | "db.m7a.medium":390000000, 180 | "db.m7a.large":781000000, 181 | "db.m7a.xlarge":1562000000, 182 | "db.m7a.2xlarge":3125000000, 183 | "db.m7a.4xlarge":6250000000, 184 | "db.m7g.medium":520000000, 185 | "db.m7g.large":937000000, 186 | "db.m7g.xlarge":1876000000, 187 | "db.m7g.2xlarge":3750000000, 188 | "db.m7g.4xlarge":7500000000, 189 | "db.m7gd.medium":520000000, 190 | "db.m7gd.large":937000000, 191 | "db.m7gd.xlarge":1876000000, 192 | "db.m7gd.2xlarge":3750000000, 193 | "db.m7gd.4xlarge":7500000000, 194 | "db.m7i.large":781000000, 195 | "db.m7i.xlarge":1562000000, 196 | "db.m7i.2xlarge":3125000000, 197 | "db.m7i.4xlarge":6250000000, 198 | "db.m7i-flex.large":390000000, 199 | "db.m7i-flex.xlarge":781000000, 200 | "db.m7i-flex.2xlarge":1562000000, 201 | "db.m7i-flex.4xlarge":3125000000, 202 | "db.m7i-flex.8xlarge":6250000000, 203 | "db.t3.nano":32000000, 204 | "db.t3.micro":64000000, 205 | "db.t3.small":128000000, 206 | "db.t3.medium":256000000, 207 | "db.t3.large":512000000, 208 | "db.t3.xlarge":1024000000, 209 | "db.t3.2xlarge":2048000000, 210 | "db.t3a.nano":32000000, 211 | "db.t3a.micro":64000000, 212 | "db.t3a.small":128000000, 213 | "db.t3a.medium":256000000, 214 | "db.t3a.large":512000000, 215 | "db.t3a.xlarge":1024000000, 216 | "db.t3a.2xlarge":2048000000, 217 | "db.t4g.nano":32000000, 218 | "db.t4g.micro":64000000, 219 | "db.t4g.small":128000000, 220 | "db.t4g.medium":256000000, 221 | "db.t4g.large":512000000, 222 | "db.t4g.xlarge":1024000000, 223 | "db.t4g.2xlarge":2048000000 224 | } 225 | -------------------------------------------------------------------------------- /server/class.logging.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | //--############# CLASS : classLogging 4 | class classLogging { 5 | 6 | properties; 7 | constructor(object) { 8 | this.properties = {...object}; 9 | } 10 | 11 | 12 | //-- Write log to screen 13 | write(module,type,message) { 14 | var timestamp = new Date(); 15 | console.log({ time : timestamp.toTimeString().split(' ')[0], 16 | type : type, 17 | object : this.properties.name, 18 | instance : this.properties.instance, 19 | module : module, 20 | message : message 21 | }); 22 | } 23 | 24 | //-- Debug, write to file 25 | debug(module,type,message) { 26 | 27 | var content = "\n" + JSON.stringify({ time : (new Date()).toTimeString(), module : module, type : type, message : message }); 28 | fs.appendFile('debug.log', content, (err) => { 29 | if (err) { 30 | console.error('Error appending to file:', err); 31 | return; 32 | } 33 | }); 34 | 35 | } 36 | 37 | } 38 | 39 | 40 | module.exports = {classLogging}; -------------------------------------------------------------------------------- /server/class.update.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec; 2 | 3 | //--############# 4 | //--############# CLASS : classApplicationUpdate 5 | //--############# 6 | 7 | 8 | class classApplicationUpdate { 9 | 10 | logging = []; 11 | status = "non-started" 12 | scriptCommand = "sudo -u ec2-user sh /aws/apps/conf/update.sh" 13 | constructor(object) { 14 | 15 | } 16 | 17 | //-- StartUpdate 18 | startUpdate(module,type,message) { 19 | 20 | this.status = "started"; 21 | this.logging = []; 22 | const objectShell = exec(this.scriptCommand); 23 | objectShell.stdout.on('data', (data)=>{ 24 | if (data !== "") { 25 | this.logging.unshift({ timestamp : new Date().toLocaleString(), message : data }); 26 | this.status = "in-progress"; 27 | } 28 | }); 29 | 30 | objectShell.stderr.on('data', (data)=>{ 31 | if (data !== "") { 32 | this.logging.unshift({ timestamp : new Date().toLocaleString(), message : data }); 33 | this.status = "in-progress"; 34 | } 35 | }); 36 | 37 | objectShell.on('close', (code) => { 38 | this.status = "completed"; 39 | }); 40 | 41 | } 42 | 43 | 44 | //-- Get Update Status 45 | getUpdateLog(){ 46 | 47 | return this.logging; 48 | 49 | } 50 | 51 | 52 | } 53 | 54 | module.exports = { classApplicationUpdate }; -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "devStart": "nodemon index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@aws-sdk/client-bedrock-runtime": "^3.556.0", 15 | "@aws-sdk/client-cloudwatch": "^3.496.0", 16 | "@aws-sdk/client-cloudwatch-logs": "^3.496.0", 17 | "@aws-sdk/client-docdb": "^3.496.0", 18 | "@aws-sdk/client-docdb-elastic": "^3.496.0", 19 | "@aws-sdk/client-dynamodb": "^3.496.0", 20 | "@aws-sdk/client-elasticache": "^3.496.0", 21 | "@aws-sdk/client-memorydb": "^3.496.0", 22 | "@aws-sdk/client-rds": "^3.682.0", 23 | "@aws-sdk/client-sts": "^3.496.0", 24 | "axios": "^1.5.0", 25 | "body-parser": "^1.20.2", 26 | "cookie-parser": "^1.4.6", 27 | "cors": "^2.8.5", 28 | "csurf": "^1.11.0", 29 | "express": "^4.18.2", 30 | "jsonwebtoken": "^9.0.0", 31 | "jwk-to-pem": "^2.0.5", 32 | "mongodb": "^6.1.0", 33 | "mssql": "^9.1.2", 34 | "mysql": "^2.18.1", 35 | "mysql2": "^3.6.1", 36 | "node-schedule": "^2.1.1", 37 | "oracledb": "^6.0.3", 38 | "pg": "^8.9.0", 39 | "postgres-pool": "^8.1.0", 40 | "redis": "^4.6.7", 41 | "redis-info": "^3.1.0", 42 | "request": "^2.88.2", 43 | "uuid": "^9.0.0" 44 | } 45 | } 46 | --------------------------------------------------------------------------------