├── .DS_Store
├── .dockerignore
├── .eslintrc.json
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── CODE_OF_CONDUCT.md
├── Contributions.md
├── Dockerfile
├── PrivacyPolicy
├── README-DeveloperGuide.md
├── README.md
├── __mocks__
├── credentialController.js
└── instancesController.js
├── babel.config.js
├── default.conf
├── docker-compose.yml
├── jest-setup.js
├── jest-teardown.js
├── jest.config.js
├── license
├── package-lock.json
├── package.json
├── server
├── .DS_Store
├── controllers
│ ├── cookieController.js
│ ├── credentialController.js
│ ├── ec2
│ │ ├── cpuMetricsController.js
│ │ ├── instancesController.js
│ │ └── networkMetricsController.js
│ ├── lambda
│ │ ├── lambdaLogsController.js
│ │ ├── lambdaMetricsController.js
│ │ └── listLambdasController.js
│ ├── rds
│ │ └── rdsMetricsController.js
│ ├── sessionController.js
│ └── userController.js
├── models
│ ├── sessionModel.js
│ └── userModel.js
└── server.js
├── src
├── App.jsx
├── Data.js
├── _variables.scss
├── componentStyling
│ ├── EC2ChartStyling.scss
│ ├── Footer.scss
│ ├── LambdaChartStyling.scss
│ ├── LandingPage.scss
│ ├── Login.scss
│ ├── Navbar.scss
│ ├── PrivacyPolicy.scss
│ ├── Settings.scss
│ └── Signup.scss
├── components
│ ├── AreaChartOptions.js
│ ├── CPUCreditBalanceChart.jsx
│ ├── CPUCreditUsageChart.jsx
│ ├── CPUSurplusCreditBalanceChart.jsx
│ ├── CPUUtilizationChart.jsx
│ ├── ColorGenerator.js
│ ├── DurationChart.jsx
│ ├── ErrorsChart.jsx
│ ├── Footer.jsx
│ ├── InvocationsChart.jsx
│ ├── LambdaLogs.jsx
│ ├── LandingPage.jsx
│ ├── LineChartOptions.js
│ ├── Login.jsx
│ ├── Navbar.jsx
│ ├── NetworkInChart.jsx
│ ├── NetworkOutChart.jsx
│ ├── PrivacyPolicy.jsx
│ ├── Settings.jsx
│ ├── Signup.jsx
│ └── ThrottlesChart.jsx
├── containerStyling
│ ├── EC2ChartContainer.scss
│ ├── LambdaChartContainer.scss
│ ├── MainContainer.scss
│ └── swirlingclouds.png
├── containers
│ ├── EC2ChartContainer.jsx
│ ├── LambdaChartContainer.jsx
│ └── MainContainer.jsx
├── index.html
├── index.js
└── styles.scss
├── test
└── Backend
│ ├── cookieController.test.js
│ ├── getCredentials.test.js
│ ├── instancesController.test.js
│ ├── jest-config-commands.js
│ ├── sessionController.test.js
│ ├── stsClient.test.js
│ └── userController.test.js
└── webpack.config.js
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/cloudband/8d8ed48e903bb679a6d0ccef53d42c0e7d0d3f50/.DS_Store
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | readme.md
3 | .git
4 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/test", "**/__tests__"],
4 | "env": {
5 | "node": true,
6 | "browser": true,
7 | "es2021": true
8 | },
9 | "plugins": ["react"],
10 | "extends": ["eslint:recommended", "plugin:react/recommended"],
11 | "parserOptions": {
12 | "sourceType": "module",
13 | "ecmaFeatures": {
14 | "jsx": true
15 | }
16 | },
17 | "rules": {
18 | "indent": ["warn", 2],
19 | "no-unused-vars": ["off", { "vars": "local" }],
20 | "no-case-declarations": "off",
21 | "prefer-const": "warn",
22 | "quotes": ["warn", "single"],
23 | "react/prop-types": "off",
24 | "semi": ["warn", "always"],
25 | "space-infix-ops": "warn"
26 | },
27 | "settings": {
28 | "react": { "version": "detect" }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | email.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/Contributions.md:
--------------------------------------------------------------------------------
1 | # Contributing to Cloudband
2 |
3 | First of all, thank you for your contribution.
4 |
5 | ## Reporting Bugs
6 |
7 | All code changes happen through Github Pull Requests and we actively welcome them. To submit your pull request, follow the steps below:
8 |
9 | ## Pull Requests
10 |
11 | 1. Fork and clone the repository.
12 | 2. Create your feature branch. (git checkout -b [my-new-feature])
13 | 3. Make sure to cover your code with tests and that your code is linted in accordance with our linting specifications (see coding style below).
14 | 4. Commit your changes locally (git commit -m 'Added some feature') and then push to your remote repository.
15 | 5. Submit a pull request to the _development_ branch, including a description of the work done in your pull request.
16 |
17 | Note: Any contributions you make will be under the MIT Software License and your submissions are understood to be under the same that covers the project. Please reach out to the team if you have any questions.
18 |
19 | ## Issues
20 |
21 | We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue.
22 |
23 | ## Coding Style
24 |
25 | 2 spaces for indentation rather than tabs
26 | 80 character line length
27 | Run npm run lint to comform to our lint rules
28 |
29 | ## License
30 |
31 | By contributing, you agree that your contributions will be licensed under Cloudband's MIT License.
32 |
33 | ### References
34 |
35 | This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
36 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:19.4.0
2 |
3 | WORKDIR /cloudband
4 |
5 | COPY package.json /cloudband/
6 | COPY package-lock.json /cloudband/
7 |
8 | RUN npm ci
9 |
10 |
11 | RUN ["apt-get", "update"]
12 | RUN ["apt-get", "install", "-y", "vim"]
13 |
14 | COPY .env /cloudband/
15 | COPY webpack.config.js /cloudband/
16 | COPY src /cloudband/src/
17 | COPY server /cloudband/server/
18 |
19 | RUN ["npm", "run", "build"]
20 |
21 | EXPOSE 3000
22 |
23 | CMD ["npm", "run", "start"]
24 | #CMD ["cross-env", "NODE_ENV=production", "node", "server/server.js"]
25 |
--------------------------------------------------------------------------------
/PrivacyPolicy:
--------------------------------------------------------------------------------
1 | Privacy Policy
2 |
3 | Cloudband is committed to protecting the privacy of our users. Our web app, Cloudband, is designed to provide AWS developers with the ability to visualize different AWS resource metrics in real time. We do not collect any personal information from our users.
4 |
5 | We understand the importance of user privacy, and we do not collect, store, or share any personal information from our users. Our app does not use cookies, tracking pixels, or any other form of data collection.
6 |
7 | Our app may contain links to third-party websites or services. We are not responsible for the privacy practices or content of these third-party websites or services. We encourage users to read the privacy policies of any third-party websites or services before providing any personal information.
8 |
9 | If you have any questions or concerns about our privacy policy, please contact us at [enter-email-here].
10 |
--------------------------------------------------------------------------------
/README-DeveloperGuide.md:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
An active AWS account is required in order to make full use of Cloudband’s features. It is highly suggested to make a new AWS account specifically for Cloudband if your use case is anything more than demoing the application.
30 |
31 | ## IAM Setup
32 |
33 |
In order for the Cloudband application to pull a user’s metrics, we will need to create an IAM user to programmatically access that user’s AWS.
34 |
35 |
On your AWS account, do the following:
36 |
37 |
1. Create an IAM user called cloudband-user with programmatic access (no need for this user to be able to sign in to the AWS console)
38 |
39 |
2. Attach the following policies directly to cloudband-user:
40 |
41 |
42 |
AdministratorAccess
43 |
AmazonEC2FullAccess
44 |
AmazonS3FullAccessAWS
45 |
AWSLambda_FullAccess
46 |
AWSLambdaRole
47 |
AWSSecurityHubFullAccess
48 |
CloudWatchFullAccess
49 |
CloudWatchLogsFullAccess
50 |
51 |
52 |
3. Create an access key for cloudband-user. Keep the access key and secret access key in your records - this will be used in the .env file in the Cloudband application.
55 |
56 |
57 | ## Streamlining the User Sign-Up Experience
58 |
If a user wants to allow cloudband-user to pull metrics from their account, they must create a role on their account. This role will specify that cloudband-user can access their account as well as specifying exactly what cloudband-user can do once they have gained access. An AWS Service called CloudFormation will be used to automate the creation of this role on a user’s account and streamline the user sign-up experience.
In order to allow the use of CloudFormation to automate the creation of a role, we must first provide the instruction of what that role can do. This comes in the form of a template. Create a yaml file (extension is .yml) with the following content (replacing the Principal / AWS ARN with the cloudband-user’s ARN & replacing the sts:External Id with the external ID that you generate via https://www.uuidgenerator.net/):
The template must be stored on our AWS account. The simplest way to do this is to create an S3 bucket and upload the template yaml file with the following steps:
381 |
382 |
383 |
Navigate to the AWS Service called S3.
384 |
Select Create Bucket.
385 |
Name the bucket "cloudband".
386 |
Unselect "Block all public access".
387 |
Create bucket.
388 |
Add to bucket policy the text below step 8.
389 |
Click upload and upload your created yaml file template.
390 |
In the list of objects in your S3 bucket, check off the Cloudband Template and click Copy URL.
Use the following link to allow your user to automatically create a stack. This link can be attached to the “Create New Stack” button found in the codebase (in InputToken.jsx - line 42). Add in your template URL, region, and external id into the link to ensure the stack is properly configured.
37 | Easy access and straightforward, intuitive visualization of AWS resource metrics are critical to AWS developers. It is intimidating to navigate the extensive AWS documentation and many hundreds of services, and challenging to quickly visualize metrics for these resources.
38 |
39 |
Our solution is a web application that provides comprehensive charts for direct visualization of a wide range of available EC2 metrics and Lambda functions, for those who use Lambda functions to process lifecycle events from Amazon Elastic Compute Cloud and their related EC2 resources
40 |
41 | Project Links: [Github](https://github.com/oslabs-beta/cloudband) | [Linkedin](https://www.linkedin.com/company/cloudbandec37) | [Press](will write this later this week)
42 |
43 |
44 | ## Getting started (User Guide)
45 | 🛠️
46 |
47 | Visit our [Website](https://www.cloud-band.io)
48 |
49 | 1. Existing user? You can log in using your email and password.
50 |
51 | 2. For new users, click "Create New Stack.
52 |
53 | 3. Follow the link and sign in as an IAM user. Follow the instructions on the How To page or watch our How To Create a New Stack Video Tutorial.
54 |
55 | 4. Copy and paste the unique ARN outputted from the prior step.
56 |
57 | 5. Input the ARN into the Enter ARN Here field
58 |
59 |
97 |
98 | ## Monitoring Features:
99 |
100 | 1. On the landing page, users can select the type of EC2 resources they'd like to monitor. Once selected, users can view the metrics for the selected EC2 resources.
101 |
102 |
103 |
104 |
105 |
106 | 2. By clicking on the instance id, you can easily toggle an instance's data on or off to view only what you want to see.
107 |
108 |
109 |
110 |
111 |
112 | 3. The Cloudband interface allows you to seamlessly switch between your EC2 and Lambda metrics for a more convenient way to view your data.
113 |
114 |
115 |
116 |
117 |
72 | );
73 | };
74 |
75 | export default CPUCreditBalanceChart;
76 |
--------------------------------------------------------------------------------
/src/components/CPUCreditUsageChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Chart from 'chart.js/auto';
3 | import { Line } from 'react-chartjs-2';
4 | import Options from './LineChartOptions.js';
5 | import { Colors, ColorsHalfOpacity } from './ColorGenerator.js';
6 | import '../componentStyling/EC2ChartStyling.scss';
7 |
8 | //declare a constant CPUCreditBalanceChart and set it equal to an arrow function that takes in props as a parameter
9 | const CPUCreditUsageChart = (props) => {
10 | //declare a constant chartData and set it equal to props.chartData
11 | const { chartData } = props;
12 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array
13 | const labels = chartData.timestamps
14 | .map((timestamp) => {
15 | //convert the timestamp to a date object
16 | const date = new Date(timestamp);
17 | // const month = date.getMonth() + 1;
18 | // const day = date.getDate();
19 | //declare a constant hour and set it equal to the hour of the date object
20 | let hour = date.getHours();
21 | //declare a constant minute and set it equal to the minutes of the date object
22 | let minute = date.getMinutes();
23 | //if the hour is less than 10, add a 0 to the beginning of the hour
24 | if (hour < 10) {
25 | hour = `0${hour}`;
26 | }
27 | //if the minute is less than 10, add a 0 to the beginning of the minute
28 | if (minute < 10) {
29 | minute = `0${minute}`;
30 | }
31 | //return the hour and minute
32 | return `${hour}:${minute}`;
33 | })
34 | //reverse the array in order to display the most recent data first
35 | .reverse(); //[timestamps]
36 |
37 | //declare a constant datasets and use the map method to iterate over the values array and return a new array of objects for each element in the values array
38 | const datasets = chartData.values
39 | .map((array, index) => {
40 | //return an object with the following properties: label, data, borderColor, backgroundColor, and yAxisID
41 | return {
42 | label: chartData.instanceIds[index],
43 | data: array,
44 | borderColor: Colors[index],
45 | backgroundColor: ColorsHalfOpacity[index],
46 | yAxisID: `y`,
47 | };
48 | })
49 | //reverse the array in order to display the most recent data first
50 | .reverse();
51 |
52 |
53 | //declare a constant data and set it equal to an object with the following properties: labels and datasets
54 | const data = {
55 | labels: labels, // [..]
56 | datasets: datasets, // [{..}, {..}, {..}]
57 | };
58 |
59 | //declare a constant options and set it equal to the Options function
60 | const options = Options(
61 | 'CPU Credit Usage',
62 | 'Number of credits used by each EC2 instance at 8 hour intervals for the past week.'
63 | );
64 |
65 | //return a div with a class of chart-wrapper and a Line component with the following properties: data and options
66 | return (
67 |
68 |
69 |
70 | );
71 | };
72 |
73 | export default CPUCreditUsageChart;
74 |
--------------------------------------------------------------------------------
/src/components/CPUSurplusCreditBalanceChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Chart from 'chart.js/auto';
3 | import { Line } from 'react-chartjs-2';
4 | import Options from './LineChartOptions.js';
5 | import { Colors, ColorsHalfOpacity } from './ColorGenerator.js';
6 | import '../componentStyling/EC2ChartStyling.scss';
7 |
8 | //declare a constant CPUCreditBalanceChart and set it equal to an arrow function that takes in props as a parameter
9 | const CPUSurplusCreditBalanceChart = (props) => {
10 | const { chartData } = props;
11 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array
12 | const labels = chartData.timestamps
13 | .map((timestamp) => {
14 | //convert the timestamp to a date object
15 | const date = new Date(timestamp);
16 | // const month = date.getMonth() + 1;
17 | // const day = date.getDate();
18 | //declare a constant hour and set it equal to the hour of the date object
19 | let hour = date.getHours();
20 | //declare a constant minute and set it equal to the minutes of the date object
21 | let minute = date.getMinutes();
22 | //if the hour is less than 10, add a 0 to the beginning of the hour
23 | if (hour < 10) {
24 | hour = `0${hour}`;
25 | }
26 | //if the minute is less than 10, add a 0 to the beginning of the minute
27 | if (minute < 10) {
28 | minute = `0${minute}`;
29 | }
30 | //return the hour and minute
31 | return `${hour}:${minute}`;
32 | })
33 | //reverse the array in order to display the most recent data first
34 | .reverse(); //[timestamps]
35 |
36 | //declare a constant datasets and use the map method to iterate over the values array and return a new array of objects for each element in the values array
37 | const datasets = chartData.values
38 | .map((array, index) => {
39 | //return an object with the following properties: label, data, borderColor, backgroundColor, and yAxisID
40 | return {
41 | label: chartData.instanceIds[index],
42 | data: array,
43 | borderColor: Colors[index],
44 | backgroundColor: ColorsHalfOpacity[index],
45 | yAxisID: `y`,
46 | };
47 | })
48 | //reverse the array in order to display the most recent data first
49 | .reverse();
50 |
51 | //declare a constant data and set it equal to an object with the following properties: labels and datasets
52 | const data = {
53 | labels: labels, // [..]
54 | datasets: datasets, // [{..}, {..}, {..}]
55 | };
56 |
57 | //declare a constant options and set it equal to the Options function
58 | const options = Options(
59 | 'CPU Surplus Credit Balance',
60 | 'Number of credits remaining for each EC2 instance at 8 hour intervals for the past week.'
61 | );
62 |
63 | return (
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | export default CPUSurplusCreditBalanceChart;
71 |
--------------------------------------------------------------------------------
/src/components/CPUUtilizationChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | // import Chart from 'chart.js/auto';
3 | import { Line } from 'react-chartjs-2';
4 | import Options from './LineChartOptions.js';
5 | import { Colors, ColorsHalfOpacity } from './ColorGenerator.js';
6 | import '../componentStyling/EC2ChartStyling.scss';
7 |
8 | //declare a constant CPUCreditBalanceChart and set it equal to an arrow function that takes in props as a parameter
9 | const CPUUtilizationChart = (props) => {
10 | //declare a constant chartData and set it equal to props.chartData
11 | const { chartData } = props;
12 | // console.log('cpu utilization data: ', chartData);
13 | console.log('chartData', chartData);
14 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array
15 | const labels = chartData.timestamps
16 | .map((timestamp) => {
17 | //convert the timestamp to a date object
18 | const date = new Date(timestamp);
19 | // const month = date.getMonth() + 1;
20 | // const day = date.getDate();
21 | //declare a constant hour and set it equal to the hour of the date object
22 | let hour = date.getHours();
23 | //declare a constant minute and set it equal to the minutes of the date object
24 | let minute = date.getMinutes();
25 | //if the hour is less than 10, add a 0 to the beginning of the hour
26 | if (hour < 10) {
27 | //add a 0 to the beginning of the hour
28 | hour = `0${hour}`;
29 | }
30 | //if the minute is less than 10, add a 0 to the beginning of the minute
31 | if (minute < 10) {
32 | //add a 0 to the beginning of the minute
33 | minute = `0${minute}`;
34 | }
35 | //return the hour and minute
36 | return `${hour}:${minute}`;
37 | })
38 | //reverse the array in order to display the most recent data first
39 | .reverse(); //[timestamps]
40 |
41 | //declare a constant datasets and use the map method to iterate over the values array and return a new array of objects for each element in the values array
42 | const datasets = chartData.values
43 | //reverse the array in order to display the most recent data first
44 | .map((array, index) => {
45 | //return an object with the following properties: label, data, borderColor, backgroundColor, and yAxisID in order to display the label, data, border color, background color, and y axis of the chart
46 | return {
47 | label: chartData.instanceIds[index],
48 | data: array,
49 | borderColor: Colors[index],
50 | backgroundColor: ColorsHalfOpacity[index],
51 | yAxisID: `y`,
52 | };
53 | })
54 | //reverse the array in order to display the most recent data first
55 | .reverse();
56 |
57 | //declare a constant data and set it equal to an object with the following properties: labels and datasets in order to display the labels and datasets of the chart
58 | const data = {
59 | labels: labels, // [..]
60 | datasets: datasets, // [{..}, {..}, {..}]
61 | };
62 | console.log('data in cpuchart', data);
63 | //declare a constant options and set it equal to the Options function that takes in a title and a description as parameters in order to display the title and description of the chart
64 | const options = Options(
65 | 'CPU Utilization',
66 | 'Utilization as a percentage across all EC2 instances every eight hours for the past week.'
67 | );
68 |
69 | return (
70 |
71 |
72 |
73 | );
74 | };
75 |
76 | export default CPUUtilizationChart;
77 |
--------------------------------------------------------------------------------
/src/components/ColorGenerator.js:
--------------------------------------------------------------------------------
1 | export const Colors = {
2 | 0: '#9DD3C8',
3 | 1: '#48BCCA',
4 | 2: '#318B9B',
5 | 3: '#23596B',
6 | 4: '#01293B',
7 | 5: '#BC7C9C',
8 | 6: '#A96DA3',
9 | 7: '#7A5980',
10 | 8: '#3B3B58',
11 | 9: '#BFB1C1',
12 | 10: '#B5BEC6',
13 | 11: '#C7DBE6',
14 | 12: '#B39C4D',
15 | 13: '#768948',
16 | 14: '#607744',
17 | 15: '#34623F',
18 | 16: '#F2C14E',
19 | 17: '#F78154',
20 | 18: '#B4436C',
21 | 19: '#5D2E8C',
22 | 20: '#51A3A3',
23 | 21: '#CB904D',
24 | 22: '#DFCC74',
25 | 23: '#C3E991',
26 | 24: '#80CED7',
27 | 25: '#8E6C88',
28 | 26: '#263D42',
29 | 27: '#0D1B1E',
30 | 28: '#F2C57C',
31 | 29: '#7FB685',
32 | 30: '#EF6F6C',
33 | };
34 |
35 | export const ColorsHalfOpacity = {
36 | 0: 'rgba(157, 211, 200, 0.5)',
37 | 1: 'rgba(72, 188, 202, 0.5)',
38 | 2: 'rgba(49, 139, 155, 0.5)',
39 | 3: 'rgba(35, 89, 107,0.5)',
40 | 4: 'rgba(1, 41, 59, 0.5)',
41 | 5: 'rgba(188, 124, 156, 0.5)',
42 | 6: 'rgba(169, 109, 163, 0.5)',
43 | 7: 'rgba(122, 89, 128, 0.5)',
44 | 8: 'rgba(59, 59, 88, 0.5)',
45 | 9: 'rgba(191, 177, 193, 0.5)',
46 | 10: 'rgba(181, 190, 198, 0.5)',
47 | 11: 'rgba(199, 219, 230, 0.5)',
48 | 12: 'rgba(179, 156, 77, 0.5)',
49 | 13: 'rgba(118, 137, 72, 0.5)',
50 | 14: 'rgba(96, 119, 68, 0.5)',
51 | 15: 'rgba(52, 98, 63, 0.5)',
52 | 16: 'rgba(242, 193, 78, 0.5)',
53 | 17: 'rgba(247, 129, 84, 0.5)',
54 | 18: 'rgba(180, 67, 108, 0.5)',
55 | 19: 'rgba(93, 46, 140, 0.5)',
56 | 20: 'rgba(81, 163, 163, 0.5)',
57 | 21: 'rgba(203, 144, 77, 0.5)',
58 | 22: 'rgba(223, 204, 116, 0.5)',
59 | 23: 'rgba(195, 233, 145, 0.5)',
60 | 24: 'rgba(128, 206, 215, 0.5)',
61 | 25: 'rgba(142, 108, 136, 0.5)',
62 | 26: 'rgba(38, 61, 66, 0.5)',
63 | 27: 'rgba(13, 27, 30, 0.5)',
64 | 28: 'rgba(242, 197, 124, 0.5)',
65 | 29: 'rgba(127, 182, 133, 0.5)',
66 | 30: 'rgba(239, 111, 108, 0.5)',
67 | };
68 |
--------------------------------------------------------------------------------
/src/components/DurationChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Filler,
11 | Legend,
12 | } from 'chart.js';
13 | import { Line } from 'react-chartjs-2';
14 | import Options from './AreaChartOptions.js';
15 | import '../componentStyling/LambdaChartStyling.scss';
16 |
17 | //declare a constant DurationChart and set it equal to an arrow function that takes in props as a parameter in order to access the chartData prop
18 | const DurationChart = (props) => {
19 | const { chartData } = props;
20 |
21 | //declare a constant options and set it equal to the Options function in order to set the options for the chart
22 | const options = Options(
23 | 'Duration',
24 | 'Average processing duration for this function every hour for the past week.'
25 | );
26 |
27 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array in order to display the timestamps on the x-axis
28 | const labels = chartData.timestamps
29 | .map((timestamp) => {
30 | //convert the timestamp to a date object
31 | const date = new Date(timestamp);
32 | //declare a constant month and set it equal to the month of the date object
33 | const month = date.getMonth() + 1;
34 | //declare a constant day and set it equal to the day of the date object
35 | const day = date.getDate();
36 | //declare a constant hour and set it equal to the hour of the date object
37 | let hour = date.getHours();
38 | //declare a constant minute and set it equal to the minutes of the date object
39 | let minute = date.getMinutes();
40 | //if the hour is less than 10, add a 0 to the beginning of the hour
41 | if (hour < 10) {
42 | hour = `0${hour}`;
43 | }
44 | //if the minute is less than 10, add a 0 to the beginning of the minute
45 | if (minute < 10) {
46 | //add a 0 to the beginning of the minute in order to display the time in the format of 00:00
47 | minute = `0${minute}`;
48 | }
49 | //return the month, day, hour, and minute in order to display the time in the format of 00/00 00:00
50 | return `${month}/${day} ${hour}:${minute}`;
51 | })
52 | //reverse the array in order to display the most recent data first
53 | .reverse(); //[timestamps]
54 |
55 | //declare a constant data and set it equal to an object with the following properties: labels and datasets in order to display the data on the chart
56 | const data = {
57 | labels,
58 | datasets: [
59 | {
60 | fill: true,
61 | label: 'Duration',
62 | data: chartData.values,
63 | borderColor: 'rgb(153, 102, 255)',
64 | backgroundColor: 'rgba(153, 102, 255, 0.5)',
65 | },
66 | ],
67 | };
68 |
69 | return (
70 |
71 |
72 |
73 | );
74 | };
75 |
76 | export default DurationChart;
77 |
--------------------------------------------------------------------------------
/src/components/ErrorsChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Filler,
11 | Legend,
12 | } from 'chart.js';
13 | import { Line } from 'react-chartjs-2';
14 | import Options from './AreaChartOptions.js';
15 | import '../componentStyling/LambdaChartStyling.scss';
16 |
17 | //declare a constant DurationChart and set it equal to an arrow function that takes in props as a parameter in order to access the chartData prop
18 | const ErrorsChart = (props) => {
19 | const { chartData } = props;
20 |
21 | //declare a constant options and set it equal to the Options function in order to set the options for the chart
22 | const options = Options(
23 | 'Errors',
24 | 'Total errors for this function every hour for the past week.'
25 | );
26 |
27 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array in order to display the timestamps on the x-axis
28 | const labels = chartData.timestamps
29 | .map((timestamp) => {
30 | const date = new Date(timestamp);
31 | const month = date.getMonth() + 1;
32 | const day = date.getDate();
33 | let hour = date.getHours();
34 | let minute = date.getMinutes();
35 | //add a 0 to the beginning of the hour in order to display the time in the format of 00:00
36 | if (hour < 10) {
37 | hour = `0${hour}`;
38 | }
39 | //add a 0 to the beginning of the minute in order to display the time in the format of 00:00
40 | if (minute < 10) {
41 | minute = `0${minute}`;
42 | }
43 | //return the month, day, hour, and minute in order to display the time in the format of 00/00 00:00
44 | return `${month}/${day} ${hour}:${minute}`;
45 | })
46 | //reverse the array in order to display the most recent data first
47 | .reverse(); //[timestamps]
48 |
49 | //declare a constant data and set it equal to an object with the following properties: labels and datasets in order to display the data on the chart
50 | const data = {
51 | labels,
52 | datasets: [
53 | {
54 | fill: true,
55 | label: 'Errors',
56 | data: chartData.values,
57 | borderColor: 'rgb(75, 192, 192)',
58 | backgroundColor: 'rgba(75, 192, 192, 0.5)',
59 | },
60 | ],
61 | };
62 |
63 | //return the Line component in order to display the chart
64 | return (
65 |
66 |
67 |
68 | );
69 | };
70 |
71 | export default ErrorsChart;
72 |
--------------------------------------------------------------------------------
/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useNavigate } from 'react-router-dom';
3 | import '../componentStyling/Footer.scss';
4 |
5 | //declare a constant Footer and declare constants navigate and redirectToPrivacyPolicy and set them equal to useNavigate and an arrow function that returns navigate('/privacy-policy')
6 | const Footer = () => {
7 | const navigate = useNavigate();
8 | const redirectToPrivacyPolicy = () => {
9 | return navigate('/privacy-policy');
10 | };
11 |
12 | //return the following JSX
13 | return (
14 |
15 |
16 |
17 |
Cloudband
18 |
19 | Cloudband is open source. Help us make our app better:
20 |
21 | Github
22 |
23 |
42 | );
43 | };
44 |
45 | export default Footer;
46 |
--------------------------------------------------------------------------------
/src/components/InvocationsChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Filler,
11 | Legend,
12 | } from 'chart.js';
13 | import { Line } from 'react-chartjs-2';
14 | import Options from './AreaChartOptions.js';
15 | import '../componentStyling/LambdaChartStyling.scss';
16 |
17 | //declare a constant InvocationsChart and set it equal to an arrow function that takes in props as a parameter in order to access the chartData prop
18 | const InvocationsChart = (props) => {
19 | const { chartData } = props;
20 | //declare a constant options and set it equal to the Options function in order to set the options for the chart
21 | const options = Options(
22 | 'Invocations',
23 | 'The sum of invocations of this function every hour for the past week.'
24 | );
25 |
26 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array in order to display the timestamps on the x-axis
27 | const labels = chartData.timestamps
28 | .map((timestamp) => {
29 | const date = new Date(timestamp);
30 | const month = date.getMonth() + 1;
31 | const day = date.getDate();
32 | let hour = date.getHours();
33 | let minute = date.getMinutes();
34 | //if the hour is less than 10, add a 0 to the beginning of the hour
35 | if (hour < 10) {
36 | hour = `0${hour}`;
37 | }
38 | //if the minute is less than 10, add a 0 to the beginning of the minute
39 | if (minute < 10) {
40 | minute = `0${minute}`;
41 | }
42 | //return the month, day, hour, and minute in order to display the time in the format of 00/00 00:00
43 | return `${month}/${day} ${hour}:${minute}`;
44 | })
45 | //reverse the array in order to display the most recent data first
46 | .reverse(); //[timestamps]
47 |
48 | //declare a constant data and set it equal to an object with the following properties: labels and datasets in order to display the data on the chart
49 | const data = {
50 | labels,
51 | datasets: [
52 | {
53 | fill: true,
54 | label: 'Invocations',
55 | data: chartData.values,
56 | borderColor: 'rgb(53, 162, 235)',
57 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
58 | },
59 | ],
60 | };
61 |
62 | return (
63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default InvocationsChart;
70 |
--------------------------------------------------------------------------------
/src/components/LambdaLogs.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Paper from '@mui/material/Paper';
3 | import Table from '@mui/material/Table';
4 | import TableBody from '@mui/material/TableBody';
5 | import TableCell from '@mui/material/TableCell';
6 | import TableContainer from '@mui/material/TableContainer';
7 | import TableHead from '@mui/material/TableHead';
8 | import TablePagination from '@mui/material/TablePagination';
9 | import TableRow from '@mui/material/TableRow';
10 | import Typography from '@mui/material/Typography';
11 |
12 | //declare a constant columns and set it equal to an array of objects with the following properties: id, label, minWidth, format
13 | const columns = [
14 | {
15 | id: 'timestamp',
16 | label: 'TimeStamp',
17 | minWidth: 270,
18 | format: (value) => value.toLocaleString('en-US'),
19 | },
20 | {
21 | id: 'message',
22 | label: 'Logs',
23 | minWidth: 400,
24 | align: 'left',
25 | format: (value) => value.toLocaleString('en-US'),
26 | },
27 | ];
28 |
29 | //declare a constant LambdaLogsTable and set it equal to an arrow function that takes in props as a parameter
30 | const LambdaLogsTable = (props) => {
31 | const { logs } = props;
32 |
33 | //declare a constant formattedLogs and set it equal to the map method to iterate over the logs array and return a new array of objects for each element in the logs array
34 | const formattedLogs = logs.map((log) => {
35 | const message = log.message;
36 | const date = new Date(log.timestamp);
37 | const month = date.getMonth() + 1;
38 | const day = date.getDate();
39 | let hour = date.getHours();
40 | let minute = date.getMinutes();
41 | //if the hour is less than 10, add a 0 to the beginning of the hour
42 | if (hour < 10) {
43 | hour = `0${hour}`;
44 | }
45 | //if the minute is less than 10, add a 0 to the beginning of the minute
46 | if (minute < 10) {
47 | minute = `0${minute}`;
48 | }
49 | //return an object with the following properties: message and timestamp
50 | return {
51 | message,
52 | timestamp: `${month}/${day} ${hour}:${minute}`,
53 | };
54 | });
55 | //declare a constant rows and set it equal to the value of formattedLogs
56 | const rows = formattedLogs;
57 | //declare a constant [page, setPage] and set it equal to React.useState and pass in 0 as an argument
58 | const [page, setPage] = React.useState(0);
59 | const [rowsPerPage, setRowsPerPage] = React.useState(10);
60 | //declare a constant handleChangePage and set it equal to an arrow function that takes in event and newPage as parameters
61 | const handleChangePage = (event, newPage) => {
62 | setPage(newPage);
63 | };
64 | //declare a constant handleChangeRowsPerPage and set it equal to an arrow function that takes in event as a parameter
65 | const handleChangeRowsPerPage = (event) => {
66 | setRowsPerPage(+event.target.value);
67 | setPage(0);
68 | };
69 |
70 | return (
71 |
79 |
80 |
94 | Logs
95 |
96 |
107 | {featureBox(
108 | ,
109 | 'Your most recent data at your fingertips',
110 | 'With each login, your most recent data will be fetched and displayed.'
111 | )}
112 | {featureBox(
113 | ,
114 | 'Compare metrics across instances',
115 | 'Hide or display instances to compare only the data you want to see.'
116 | )}
117 | {featureBox(
118 | ,
119 | 'Switch between EC2 or Lambda data with just a click',
120 | 'Toggle between views to see all your data in the same console.'
121 | )}
122 |
123 |
124 |
125 | {highlight(
126 | 'text-left',
127 | 'Conventiently compiled EC2 Metric Datasets',
128 | 'With two tailored views, you can see all of your CPU or Network In/Out data for all of your EC2 Instances at a glance.',
129 | 'https://cloudband.s3.amazonaws.com/Cloudband_toggle-ec2-metrics-cropped.gif',
130 | 'toggle-ec2-metrics-gif'
131 | )}
132 | {highlight(
133 | 'text-right',
134 | 'Compare between all EC2 instances or just the ones you want to see',
135 | "By clicking on the instance id, you can easily toggle an instance's data on or off to view only what you want to see.",
136 | 'https://cloudband.s3.amazonaws.com/Cloudband_toggleEC2instances_cropped.gif',
137 | 'toggle-ec2-instances-gif'
138 | )}
139 | {highlight(
140 | 'text-left',
141 | 'Easily toggle between EC2 Metrics and Lambda functions',
142 | 'The Cloudband interface allows you to seamlessly switch between your EC2 and Lambda metrics for a more convenient way to view your data.',
143 | 'https://cloudband.s3.amazonaws.com/Cloudband-toggle-lambda-functions.gif',
144 | 'placeholder image'
145 | )}
146 |
57 | );
58 | }
59 | };
60 |
61 | export default Login;
62 |
--------------------------------------------------------------------------------
/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useNavigate } from 'react-router-dom';
3 | import axios from 'axios';
4 | import '../componentStyling/Navbar.scss';
5 |
6 | //declare a constant Navbar and set it equal to an arrow function that takes in props as a parameter
7 | function Navbar(props) {
8 | //declare a constant navigate and set it equal to the invocation of the useNavigate hook
9 | const navigate = useNavigate();
10 | //declare a constant loggedIn and set it equal to the value of the loggedIn property on the props object
11 | const { loggedIn, setLoggedIn } = props;
12 |
13 | //declare a constant logoutUser and set it equal to an arrow function that takes in event as a parameter
14 | const logoutUser = (event) => {
15 | //make a request to the /logout endpoint and then invoke the setLoggedIn function and pass in false as an argument and then return the invocation of the navigate function and pass in '/login' as an argument
16 | axios.delete('/logout').then(() => {
17 | setLoggedIn(false);
18 | return navigate('/login');
19 | });
20 | };
21 |
22 | //declare a constant redirectToVisualizer and set it equal to an arrow function that returns the invocation of the navigate function and pass in '/visualizer' as an argument
23 | const redirectToVisualizer = () => {
24 | return navigate('/visualizer');
25 | };
26 |
27 | //if loggedIn is truthy then return the following JSX
28 | if (loggedIn) {
29 | return (
30 |
88 | );
89 | }
90 | }
91 |
92 | export default Navbar;
93 |
--------------------------------------------------------------------------------
/src/components/NetworkInChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Chart from 'chart.js/auto';
3 | import { Line } from 'react-chartjs-2';
4 | import Options from './LineChartOptions.js';
5 | import { Colors, ColorsHalfOpacity } from './ColorGenerator.js';
6 | import '../componentStyling/EC2ChartStyling.scss';
7 |
8 | //declare a constant networkInChart and set it equal to an arrow function that takes in props as a parameter
9 | const NetworkInChart = (props) => {
10 | //declare a constant chartData and set it equal to props.chartData
11 | const { chartData } = props;
12 |
13 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array
14 | const labels = chartData.timestamps
15 | .map((timestamp) => {
16 | //convert the timestamp to a date object
17 | const date = new Date(timestamp);
18 | // const month = date.getMonth() + 1;
19 | // const day = date.getDate();
20 | let hour = date.getHours();
21 | let minute = date.getMinutes();
22 | //if the hour is less than 10, add a 0 to the beginning of the hour
23 | if (hour < 10) {
24 | hour = `0${hour}`;
25 | }
26 | //if the minute is less than 10, add a 0 to the beginning of the minute
27 | if (minute < 10) {
28 | minute = `0${minute}`;
29 | }
30 | //return the hour and minute
31 | return `${hour}:${minute}`;
32 | })
33 | //reverse the array in order to display the most recent data first
34 | .reverse(); //[timestamps]
35 |
36 | //declare a constant datasets and use the map method to iterate over the values array and return a new array of objects for each element in the values array
37 | const datasets = chartData.values
38 | .map((array, index) => {
39 | return {
40 | label: chartData.instanceIds[index],
41 | data: array,
42 | borderColor: Colors[index],
43 | backgroundColor: ColorsHalfOpacity[index],
44 | yAxisID: `y`,
45 | };
46 | })
47 | //reverse the array in order to display the most recent data first
48 | .reverse();
49 |
50 | //declare a constant data and set it equal to an object with the following properties: labels and datasets
51 | const data = {
52 | labels: labels, // [..]
53 | datasets: datasets, // [{..}, {..}, {..}]
54 | };
55 |
56 | //declare a constant options in order to set the options for the chart
57 | const options = Options(
58 | 'Network In',
59 | 'Number of bytes sent in for each EC2 instance at 8 hour intervals for the past week.',
60 | 'EC2 Instance Ids'
61 | );
62 |
63 | return (
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | export default NetworkInChart;
71 |
--------------------------------------------------------------------------------
/src/components/NetworkOutChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Chart from 'chart.js/auto';
3 | import { Line } from 'react-chartjs-2';
4 | import Options from './LineChartOptions.js';
5 | import { Colors, ColorsHalfOpacity } from './ColorGenerator.js';
6 | import '../componentStyling/EC2ChartStyling.scss';
7 |
8 |
9 | //declare a constant NetworkOutChart and set it equal to an arrow function that takes in props as a parameter
10 | const NetworkOutChart = (props) => {
11 | //declare a constant chartData and set it equal to props.chartData
12 | const { chartData } = props;
13 |
14 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the timestamps array
15 | const labels = chartData.timestamps
16 | .map((timestamp) => {
17 | //convert the timestamp to a date object
18 | const date = new Date(timestamp);
19 | // const month = date.getMonth() + 1;
20 | // const day = date.getDate();
21 | let hour = date.getHours();
22 | let minute = date.getMinutes();
23 | //if the hour is less than 10, add a 0 to the beginning of the hour
24 | if (hour < 10) {
25 | hour = `0${hour}`;
26 | }
27 | //if the minute is less than 10, add a 0 to the beginning of the minute
28 | if (minute < 10) {
29 | minute = `0${minute}`;
30 | }
31 | //return the hour and minute
32 | return `${hour}:${minute}`;
33 | })
34 | //reverse the array in order to display the most recent data first
35 | .reverse(); //[timestamps]
36 |
37 | //declare a constant datasets and use the map method to iterate over the values array and return a new array of objects for each element in the values array
38 | const datasets = chartData.values
39 | .map((array, index) => {
40 | return {
41 | label: chartData.instanceIds[index],
42 | data: array,
43 | borderColor: Colors[index],
44 | backgroundColor: ColorsHalfOpacity[index],
45 | yAxisID: `y`,
46 | };
47 | })
48 | //reverse the array in order to display the most recent data first
49 | .reverse();
50 |
51 | //declare a constant data and set it equal to an object with the following properties: labels and datasets
52 | const data = {
53 | labels: labels, // [..]
54 | datasets: datasets, // [{..}, {..}, {..}]
55 | };
56 |
57 | //declare a constant options in order to set the options for the chart
58 | const options = Options(
59 | 'Network Out',
60 | 'Number of bytes sent out for each EC2 instance at 8 hour intervals for the past week.',
61 | 'EC2 Instance Ids'
62 | );
63 |
64 | //return the chart
65 | return (
66 |
9 | Cloudband is committed to protecting the privacy of our users. Our web
10 | app, Cloudband, is designed to provide AWS developers with the ability
11 | to visualize different AWS resource metrics in real time. We do not
12 | collect any personal information from our users.
13 |
14 |
15 | We understand the importance of user privacy, and we do not collect,
16 | store, or share any personal information from our users. Our app does
17 | not use cookies, tracking pixels, or any other form of data collection.
18 |
19 |
20 | Our app may contain links to third-party websites or services. We are
21 | not responsible for the privacy practices or content of these
22 | third-party websites or services.
23 |
24 |
25 | We encourage users to read the privacy policies of any third-party
26 | websites or services before providing any personal information. If you
27 | have any questions or concerns about our privacy policy, please contact
28 | us at
29 | cloudbandEC37@gmail.com.
30 |
31 |
32 | );
33 | };
34 |
35 | export default PrivacyPolicy;
36 |
--------------------------------------------------------------------------------
/src/components/Settings.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import axios from 'axios';
3 | import '../componentStyling/Settings.scss';
4 |
5 | const Settings = (props) => {
6 | const {
7 | ec2Metric,
8 | setEc2Metric,
9 | tool,
10 | setTool,
11 | funcNames,
12 | setFuncNames,
13 | arn,
14 | region,
15 | currFunc,
16 | setCurrFunc,
17 | } = props;
18 |
19 | // sets ec2 metric based on drop down select
20 | const onEC2MetricChange = (event) => {
21 | setEc2Metric(event.target.value);
22 | };
23 |
24 | // changes between showing ec2 or lambda metrics/options based on drop down select
25 | const onToolChange = (event) => {
26 | setTool(event.target.value);
27 | };
28 |
29 | // sets current lambda function based on drop down select
30 | const onCurrFuncChange = (event) => {
31 | setCurrFunc(event.target.value);
32 | };
33 |
34 | // fetches lambda names when a user selects lambda to populate drop down and set the current lambda function
35 | useEffect(() => {
36 | axios
37 | .get(`http://localhost:3000/getLambdaNames`, {
38 | params: {
39 | arn,
40 | region,
41 | },
42 | })
43 | .then((response) => {
44 | console.log(
45 | 'lambda names response from Settings component axios call: ',
46 | response
47 | );
48 | setFuncNames(response.data);
49 | setCurrFunc(response.data[0]);
50 | })
51 | .catch((err) => {
52 | console.log(err);
53 | });
54 | }, [tool]);
55 |
56 | // toggles showing ec2 options or lambda options based on drop down select
57 | function switchSettings() {
58 | if (tool === 'ec2') {
59 | return (
60 |
61 |
62 |
63 |
72 |
73 |
Tip:
74 |
75 | Once your metrics have loaded, try clicking on the EC2 Instance Id's
76 | to add/remove the instance from your view.
77 |
112 | Use the dropdowns below to choose which AWS tool and specific metrics
113 | you would like to view.
114 |
115 |
116 |
117 |
121 |
122 |
{switchSettings()}
123 |
124 | );
125 | };
126 |
127 | export default Settings;
128 |
--------------------------------------------------------------------------------
/src/components/Signup.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Navigate, useNavigate } from 'react-router-dom';
3 | import axios from 'axios';
4 | import '../componentStyling/Signup.scss';
5 | //import AWS SDK
6 | const AWS = require('aws-sdk');
7 | //set the region
8 | AWS.config.update({ region: 'REGION' });
9 |
10 | //declare a constant Signup in order to create a new user.
11 | const Signup = () => {
12 | //declare a constant navigate and set it equal to the invocation of the useNavigate hook
13 | const navigate = useNavigate();
14 | //declare a constant redirectToPrivacyPolicy and set it equal to an arrow function that returns the invocation of the navigate function and pass in '/privacy-policy' as an argument
15 | const redirectToPrivacyPolicy = () => {
16 | //return the invocation of the navigate function and pass in '/privacy-policy' as an argument
17 | return navigate('/privacy-policy');
18 | };
19 |
20 | //declare a constant [signedUp, setSignedUp] and set it equal to the invocation of the useState hook and pass in false as an argument
21 | const [signedUp, setSignedUp] = useState(false);
22 | //declare a constant [email, setEmail] and set it equal to the invocation of the useState hook and pass in an empty string as an argument
23 | const [email, setEmail] = useState('');
24 | //declare a constant [password, setPassword] and set it equal to the invocation of the useState hook and pass in an empty string as an argument
25 | const [password, setPassword] = useState('');
26 | //declare a constant [arn, setArn] and set it equal to the invocation of the useState hook and pass in an empty string as an argument
27 | const [arn, setArn] = useState('');
28 | //declare a constant [region, setRegion] and set it equal to the invocation of the useState hook and pass in 'us-east-1' as an argument
29 | const [region, setRegion] = useState('us-east-1');
30 |
31 | // request to server to add a user to the database and then route to login page
32 | const handleSubmit = (event) => {
33 | event.preventDefault();
34 | //make a request to the /signup endpoint and then invoke the setSignedUp function and pass in true as an argument
35 | axios
36 | .post('/signup', {
37 | email: email,
38 | password: password,
39 | RoleARN: arn,
40 | region: region,
41 | })
42 | .then((response) => {
43 | setSignedUp(true);
44 | })
45 | .catch((err) => {
46 | console.log('error in sign up request: ', err);
47 | });
48 | };
49 | //if signedUp is truthy then return the following JSX
50 | if (signedUp) {
51 | return ;
52 | }
53 | //if signedUp is falsy then return the following JSX
54 | if (!signedUp) {
55 | return (
56 |
57 |
58 |
59 |
Get Started!
60 |
Sign up in just a few quick steps.
61 |
62 | Follow this tutorial video on how to create a stack and generate
63 | the ARN.
This will grant Cloudband access to AWS
64 | metrics. For more information on what data we capture, please
65 | refer to our
66 | {/* privacy policy. */}
67 |
70 |
195 | );
196 | }
197 | };
198 |
199 | export default Signup;
200 |
--------------------------------------------------------------------------------
/src/components/ThrottlesChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Filler,
11 | Legend,
12 | } from 'chart.js';
13 | import { Line } from 'react-chartjs-2';
14 | import Options from './AreaChartOptions.js';
15 | import '../componentStyling/LambdaChartStyling.scss';
16 |
17 | //declare a constant ThrottlesChart, pass in props as a parameter. Destructure chartData from props in order to access the chartData prop
18 | const ThrottlesChart = (props) => {
19 | const { chartData } = props;
20 |
21 | //declare a constant options and set it equal to the Options function in order to set the options for the chart
22 | const options = Options(
23 | 'Throttles',
24 | 'The sum of throttles on this function every hour for the past week.'
25 | );
26 |
27 | //declare a constant labels and use the map method to iterate over the timestamps array and return a new array of timestamps for each element in the array.
28 | const labels = chartData.timestamps
29 | .map((timestamp) => {
30 | //declare a constant date and set it equal to a new Date object with the timestamp as a parameter
31 | const date = new Date(timestamp);
32 | //declare a constant month and set it equal to the month of the date object plus 1 in order to display the correct month
33 | const month = date.getMonth() + 1;
34 | //declare a constant day and set it equal to the day of the date object
35 | const day = date.getDate();
36 | //declare a constant hour and set it equal to the hour of the date object
37 | let hour = date.getHours();
38 | //declare a constant minute and set it equal to the minutes of the date object
39 | let minute = date.getMinutes();
40 | //if the hour is less than 10, add a 0 to the beginning of the hour
41 | if (hour < 10) {
42 | hour = `0${hour}`;
43 | }
44 | //if the minute is less than 10, add a 0 to the beginning of the minute
45 | if (minute < 10) {
46 | minute = `0${minute}`;
47 | }
48 | //return the month, day, hour, and minute in order to display the time in the format of 00/00 00:00
49 | return `${month}/${day} ${hour}:${minute}`;
50 | })
51 | //reverse the array in order to display the most recent data first
52 | .reverse(); //[timestamps]
53 |
54 | //declare a constant data and set it equal to an object with the following properties: labels and datasets in order to display the data on the chart
55 | const data = {
56 | labels,
57 | datasets: [
58 | {
59 | fill: true,
60 | label: 'Throttles',
61 | data: chartData.values,
62 | borderColor: 'rgb(201, 203, 207)',
63 | backgroundColor: 'rgba(201, 203, 207, 0.5)',
64 | },
65 | ],
66 | };
67 |
68 | return (
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | export default ThrottlesChart;
76 |
--------------------------------------------------------------------------------
/src/containerStyling/EC2ChartContainer.scss:
--------------------------------------------------------------------------------
1 | .chart-container-wrapper {
2 | display: flex;
3 | flex-wrap: wrap;
4 | justify-content: center;
5 | background-color: var(--light-color);
6 | min-height: 800px;
7 |
8 | .row {
9 | display: flex;
10 | flex-direction: row;
11 | flex-wrap: wrap;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/containerStyling/LambdaChartContainer.scss:
--------------------------------------------------------------------------------
1 | .lambda-chart-container {
2 | display: flex;
3 | flex-wrap: wrap;
4 |
5 | justify-content: center;
6 | background-color: var(--light-color);
7 | min-height: 800px;
8 |
9 | .row {
10 | display: flex;
11 | flex-direction: row;
12 | flex-wrap: wrap;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/containerStyling/MainContainer.scss:
--------------------------------------------------------------------------------
1 | .main-container-wrapper {
2 | display: flex;
3 | flex-direction: row;
4 | // border: 1px solid black;
5 | min-height: 693px;
6 | }
7 |
--------------------------------------------------------------------------------
/src/containerStyling/swirlingclouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/cloudband/8d8ed48e903bb679a6d0ccef53d42c0e7d0d3f50/src/containerStyling/swirlingclouds.png
--------------------------------------------------------------------------------
/src/containers/EC2ChartContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import axios from 'axios';
3 | import CPUUtilizationChart from '../components/CPUUtilizationChart.jsx';
4 | import NetworkInChart from '../components/NetworkInChart.jsx';
5 | import NetworkOutChart from '../components/NetworkOutChart.jsx';
6 | import CPUCreditUsageChart from '../components/CPUCreditUsageChart.jsx';
7 | import CPUCreditBalanceChart from '../components/CPUCreditBalanceChart.jsx';
8 | import CPUSurplusCreditBalanceChart from '../components/CPUSurplusCreditBalanceChart.jsx';
9 | import '../containerStyling/EC2ChartContainer.scss';
10 |
11 | //declare a constant defaultDataStructure and set it equal to an object with the following properties: values, timestamps, and instanceIds
12 | const defaultDataStructure = {
13 | values: [],
14 | timestamps: [],
15 | instanceIds: [],
16 | };
17 |
18 | //declare a constant EC2ChartContainer and set it equal to an arrow function that takes in props as a parameter
19 | const EC2ChartContainer = (props) => {
20 | //declare a constant ec2Metric and set it equal to props.ec2Metric
21 | const { ec2Metric, arn, region } = props;
22 | //declare a constant [cpuUtilizationData, setCpuUtilizationData] and set it equal to useState and pass in defaultDataStructure as an argument
23 | const [cpuUtilizationData, setCpuUtilizationData] =
24 | useState(defaultDataStructure);
25 | //declare a constant [networkInData, setNetworkInData] and set it equal to useState and pass in defaultDataStructure as an argument
26 | const [networkInData, setNetworkInData] = useState(defaultDataStructure);
27 | //declare a constant [networkOutData, setNetworkOutData] and set it equal to useState and pass in defaultDataStructure as an argument
28 | const [networkOutData, setNetworkOutData] = useState(defaultDataStructure);
29 | //declare a constant [cpuCreditUsageData, setCpuCreditUsageData] and set it equal to useState and pass in defaultDataStructure as an argument
30 | const [cpuCreditUsageData, setCpuCreditUsageData] =
31 | useState(defaultDataStructure);
32 | //declare a constant [cpuCreditBalanceData, setCpuCreditBalanceData] and set it equal to useState and pass in defaultDataStructure as an argument
33 | const [cpuCreditBalanceData, setCpuCreditBalanceData] =
34 | useState(defaultDataStructure);
35 | //declare a constant [cpuSurplusCreditBalanceData, setCpuSurplusCreditBalanceData] and set it equal to useState and pass in defaultDataStructure as an argument
36 | const [cpuSurplusCreditBalanceData, setCpuSurplusCreditBalanceData] =
37 | useState(defaultDataStructure);
38 |
39 | //declare a useEffect hook
40 | useEffect(() => {
41 | //declare a constant fetchCloudwatchData and set it equal to an async function
42 | const fetchCloudwatchData = async () => {
43 | //declare a try/catch block
44 | try {
45 | //declare a constant response and set it equal to await axios.get and pass in the following arguments: `http://localhost:3000/${ec2Metric}`, and an object with the following properties: params and set it equal to an object with the following properties: arn and region
46 | const response = await axios.get(`http://localhost:3000/${ec2Metric}`, {
47 | params: {
48 | arn,
49 | region,
50 | },
51 | });
52 | //if ec2Metric is equal to 'network-in-out'
53 | if (ec2Metric === 'network-in-out') {
54 | //set the state of networkInData to an object
55 | setNetworkInData({
56 | //passing in all the properties of defaultDataStructure
57 | ...defaultDataStructure,
58 | //passing in all the properties of response.data.NetworkIn or an empty object
59 | ...(response?.data?.NetworkIn ?? {}),
60 | });
61 | //set the state of networkOutData to an object
62 | setNetworkOutData({
63 | //passing in all the properties of defaultDataStructure
64 | ...defaultDataStructure,
65 | //passing in all the properties of response.data.NetworkOut or an empty object
66 | ...(response?.data?.NetworkOut ?? {}),
67 | });
68 | }
69 | //if the ec2Metric is equal to 'cpu-credits'
70 | if (ec2Metric === 'cpu-credits') {
71 | //set the state of cpuUtilizationData to an object
72 | setCpuUtilizationData({
73 | //passing in all the properties of defaultDataStructure
74 | ...defaultDataStructure,
75 | //passing in all the properties of response.data.CPUUtilization or an empty object
76 | ...(response?.data?.CPUUtilization ?? {}),
77 | });
78 | //set the state of cpuCreditUsageData to an object
79 | setCpuCreditUsageData({
80 | //passing in all the properties of defaultDataStructure
81 | ...defaultDataStructure,
82 | //passing in all the properties of response.data.CPUCreditUsage or an empty object
83 | ...(response?.data?.CPUCreditUsage ?? {}),
84 | });
85 | //set the state of cpuCreditBalanceData to an object
86 | setCpuCreditBalanceData({
87 | //passing in all the properties of defaultDataStructure
88 | ...defaultDataStructure,
89 | //passing in all the properties of response.data.CPUCreditBalance or an empty object
90 | ...(response?.data?.CPUCreditBalance ?? {}),
91 | });
92 | //set the state of cpuSurplusCreditBalanceData to an object
93 | setCpuSurplusCreditBalanceData({
94 | //passing in all the properties of defaultDataStructure
95 | ...defaultDataStructure,
96 | //passing in all the properties of response.data.CPUSurplusCreditBalance
97 | ...(response?.data?.CPUSurplusCreditBalance ?? {}),
98 | });
99 | }
100 | } catch (error) {
101 | console.error(error);
102 | }
103 | };
104 | //invoke the fetchCloudwatchData function
105 | fetchCloudwatchData();
106 | //pass in ec2Metric as a dependency
107 | }, [ec2Metric]);
108 |
109 | // renders a different chart based on the ec2Metric a user selects in settings component
110 | function switchCharts() {
111 | //if ec2Metric is equal to 'network-in-out'
112 | if (ec2Metric === 'network-in-out') {
113 | //return the following JSX
114 | return (
115 |
116 |
117 |
118 |
119 |
120 |
121 | );
122 | //otherwise, if ec2Metric is equal to 'cpu-credits'
123 | } else if (ec2Metric === 'cpu-credits') {
124 | //return the following JSX
125 | return (
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
136 |
137 |
138 | );
139 | }
140 | }
141 |
142 | return
{switchCharts()}
;
143 | };
144 |
145 | export default EC2ChartContainer;
146 |
--------------------------------------------------------------------------------
/src/containers/LambdaChartContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import axios from 'axios';
3 | import InvocationsChart from '../components/InvocationsChart.jsx';
4 | import ThrottlesChart from '../components/ThrottlesChart.jsx';
5 | import ErrorsChart from '../components/ErrorsChart.jsx';
6 | import DurationChart from '../components/DurationChart.jsx';
7 | import LambdaLogs from '../components/LambdaLogs.jsx';
8 | import '../containerStyling/LambdaChartContainer.scss';
9 |
10 | //declare a constant defaultDataStructure and set it equal to an object with the following properties: values and timestamps
11 | const defaultDataStructure = {
12 | values: [],
13 | timestamps: [],
14 | };
15 |
16 | //declare a constant LambdaChartContainer and set it equal to an arrow function that takes in props as a parameter
17 | const LambdaChartContainer = (props) => {
18 | //declare a constant {arn, currFunc, region} and set it equal to props
19 | const { arn, currFunc, region } = props;
20 | //declare a constant [invocationData, setInvocationData] and set it equal to useState and pass in defaultDataStructure as an argument
21 | const [invocationData, setInvocationData] = useState(defaultDataStructure);
22 | //declare a constant [throttleData, setThrottleData] and set it equal to useState and pass in defaultDataStructure as an argument
23 | const [throttleData, setThrottleData] = useState(defaultDataStructure);
24 | //declare a constant [errorData, setErrorData] and set it equal to useState and pass in defaultDataStructure as an argument
25 | const [errorData, setErrorData] = useState(defaultDataStructure);
26 | //declare a constant [durationData, setDurationData] and set it equal to useState and pass in defaultDataStructure as an argument
27 | const [durationData, setDurationData] = useState(defaultDataStructure);
28 | //declare a constant [lambdaLogs, setLambdaLogs] and set it equal to useState and pass in an empty array as an argument
29 | const [lambdaLogs, setLambdaLogs] = useState([]);
30 |
31 | //use the useEffect hook to make an axios request to the getLambdaMetrics endpoint
32 | useEffect(() => {
33 | axios
34 | .get(`http://localhost:3000/getLambdaMetrics`, {
35 | params: {
36 | arn,
37 | currFunc,
38 | region,
39 | },
40 | })
41 | .then((response) => {
42 | console.log(
43 | 'response from LambdaChartContainer getLambdaMetrics: ',
44 | response
45 | );
46 | //set the state of invocationData, throttleData, errorData, durationData, and lambdaLogs to the data returned from the axios request
47 | setInvocationData({
48 | ...defaultDataStructure,
49 | ...(response?.data[0] ?? {}),
50 | });
51 | setThrottleData({
52 | ...defaultDataStructure,
53 | ...(response?.data[1] ?? {}),
54 | });
55 | setErrorData({ ...defaultDataStructure, ...(response?.data[2] ?? {}) });
56 | setDurationData({
57 | ...defaultDataStructure,
58 | ...(response?.data[3] ?? {}),
59 | });
60 | setLambdaLogs(response?.data[4] ?? []);
61 | })
62 | .then(() => {
63 | console.log('durationData: ', durationData);
64 | })
65 | .catch((err) => {
66 | console.log(err);
67 | });
68 | }, [currFunc]);
69 |
70 | return (
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | };
80 |
81 | export default LambdaChartContainer;
82 |
--------------------------------------------------------------------------------
/src/containers/MainContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import EC2ChartContainer from './EC2ChartContainer.jsx';
3 | import LambdaChartContainer from './LambdaChartContainer.jsx';
4 | import Settings from '../components/Settings.jsx';
5 | import '../containerStyling/MainContainer.scss';
6 | import { Navigate } from 'react-router-dom';
7 |
8 | //declare a constant MainContainer and pass in props as a parameter
9 | const MainContainer = (props) => {
10 | //declare a constant loggedIn, arn, and region and set them equal to props.loggedIn, props.arn, and props.region
11 | const { loggedIn, arn, region } = props;
12 | //declare a constant [ec2Metric, setEc2Metric] and set it equal to useState and pass in 'cpu-credits' as an argument
13 | const [ec2Metric, setEc2Metric] = useState('cpu-credits');
14 | //declare a constant [tool, setTool] and set it equal to useState and pass in 'ec2' as an argument
15 | const [tool, setTool] = useState('ec2');
16 | //declare a constant [funcNames, setFuncNames] and set it equal to useState and pass in an empty array as an argument
17 | const [funcNames, setFuncNames] = useState([]);
18 | //declare a constant [currFunc, setCurrFunc] and set it equal to useState and pass in an empty string as an argument
19 | const [currFunc, setCurrFunc] = useState('');
20 |
21 | // renders ec2 or lambda charts/options based on drop down selection in settings
22 | function switchChartContainers() {
23 | //if tool is equal to 'ec2' then return the EC2ChartContainer component and pass in ec2Metric, arn, and region as props
24 | if (tool === 'ec2') {
25 | return (
26 |
27 | );
28 | //otherwise, if the tool is equal to 'lambda' then return the LambdaChartContainer component and pass in currFunc, arn, and region as props
29 | } else if (tool === 'lambda') {
30 | return (
31 |
32 |
33 |
34 | );
35 | }
36 | }
37 |
38 | // reroutes to login page if a user is not logged in
39 | if (!loggedIn) {
40 | return ;
41 | } else {
42 | return (
43 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import App from './App.jsx';
3 | import Login from './components/Login.jsx';
4 | import ReactDOM from 'react-dom/client';
5 | import { createBrowserRouter, RouterProvider } from 'react-router-dom';
6 | // import { render } from 'react-dom'; //--> deprecated, apparently. createRoot method (see below) is part of the new API for rendering React components in the DOM, which was introduced in React 18. This API replaces the old render method, which has been deprecated and will be removed in a future version of React.
7 | import { BrowserRouter } from 'react-router-dom';
8 |
9 | // // import './stylesheets/styles.scss';
10 | import { createRoot } from 'react-dom/client';
11 |
12 | // // //render(, document.getElementById("root"));
13 |
14 | const domNode = document.getElementById('root');
15 | const root = createRoot(domNode);
16 | root.render(
17 |
18 |
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap');
2 | @import './variables';
3 |
4 | body {
5 | // background: linear-gradient(to right, #d3d3d3, #87ceeb, #e6e6fa);
6 | // color: white;
7 | // background: linear-gradient(180deg, #5ea2ed 0%, #003566 100%);
8 | background: linear-gradient(180deg, #7cb6d6 0%, #042439 100%);
9 | // background: linear-gradient(
10 | // 180deg,
11 | // #67aefd 0%,
12 | // rgba(0, 29, 61, 0.9) 34.38%,
13 | // #67aefd 100%
14 | // );
15 | margin: 0 auto;
16 | // background: var(--light-color);
17 | font-family: Quicksand;
18 | // color: var(--mid-blue);
19 | color: var(--light-color);
20 |
21 | hr {
22 | border: 0.5px solid var(--light-color-opacity);
23 | margin: 0 auto;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Backend/cookieController.test.js:
--------------------------------------------------------------------------------
1 | const cookieController = require('../../server/controllers/cookieController.js');
2 | const User = require('../../server/models/sessionModel.js');
3 |
4 | describe('cookieController', () => {
5 | describe('setSSIDCookie', () => {
6 | // Test that a cookie is set and the next function is called without throwing an error
7 | it('should set a cookie and call the next function without throwing an error', async () => {
8 | // Initialize request and response objects
9 | const req = {};
10 | const res = {
11 | locals: {
12 | newUser: {
13 | _id: '123'
14 | }
15 | },
16 | cookie: jest.fn()
17 | };
18 | // Initialize a spy function for the next function
19 | const next = jest.fn();
20 |
21 | await cookieController.setSSIDCookie(req, res, next);
22 |
23 | // Assert that the res.cookie method was called with the correct arguments
24 | expect(res.cookie).toHaveBeenCalledWith('ssid', '123', {
25 | httpOnly: true
26 | });
27 | // Assert that the next function was called
28 | expect(next).toHaveBeenCalled();
29 | // Assert that the ssidCookie was set to the user id
30 | expect(res.locals.ssidCookie).toEqual('123');
31 | });
32 |
33 | // Test that the global error handler is called if an error is thrown while setting the cookie. Please NOTE that this test is designed to fail if the web app is not live and running
34 | it('should call the global error handler if an error is thrown while setting the cookie', async () => {
35 | // Initialize request and response objects
36 | const req = {};
37 | const res = {
38 | locals: {
39 | newUser: {
40 | _id: '123'
41 | }
42 | },
43 | cookie: jest.fn()
44 | };
45 | // Initialize a spy function for the next function
46 | const next = jest.fn();
47 | // Initialize an error object
48 | const error = new Error('Error setting cookie');
49 |
50 | // Spy on the User.findOne method and make it return a rejected promise
51 | //Note, when the web app is not live and running, this method will necessarily fail.
52 | jest.spyOn(User, 'findOne').mockRejectedValue(error);
53 |
54 | // Call the setSSIDCookie function
55 | await cookieController.setSSIDCookie(req, res, next);
56 |
57 | // Assert that the next function was called with the appropriate error object
58 | expect(next).toHaveBeenCalledWith({
59 | log: `Error in cookieController.setSSIDCookie. Details: ${error}`,
60 | message: { err: 'An error occurred in cookieController.setSSIDCookie' },
61 | });
62 | // Assert that the res.cookie method was not called
63 | expect(res.cookie).not.toHaveBeenCalled();
64 | });
65 | });
66 | });
67 |
68 |
--------------------------------------------------------------------------------
/test/Backend/getCredentials.test.js:
--------------------------------------------------------------------------------
1 | const credentialController = require('../../__mocks__/credentialController');
2 |
3 | describe('credentialController', () => {
4 | const arn = 'arn:aws:iam::123456789:role/example-role';
5 | const req = { query: { arn } };
6 | const res = { locals: {} };
7 | const next = jest.fn();
8 |
9 | beforeEach(() => {
10 | jest.clearAllMocks();
11 | });
12 |
13 | describe('getCredentials', () => {
14 | it('should get credentials', async () => {
15 | await credentialController.getCredentials(req, res, next);
16 | expect(res.locals.credentials).toEqual({
17 | accessKeyId: 'mocked-access-key-id',
18 | secretAccessKey: 'mocked-secret-access-key',
19 | sessionToken: 'mocked-session-token',
20 | });
21 | expect(next).toHaveBeenCalled();
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/Backend/instancesController.test.js:
--------------------------------------------------------------------------------
1 | import { getInstances } from '../../server/controllers/ec2/instancesController';
2 | import { EC2Client } from '@aws-sdk/client-ec2';
3 |
4 | describe('getInstances', () => {
5 | test('should call the EC2Client with the correct parameters', async () => {
6 | const req = {};
7 | const res = {
8 | locals: {
9 | credentials: {
10 | accessKeyId: 'test_access_key',
11 | secretAccessKey: 'test_secret_key',
12 | sessionToken: 'test_session_token',
13 | },
14 | },
15 | };
16 | const next = jest.fn();
17 |
18 | const ec2Client = new EC2Client({
19 | region: 'us-east-1',
20 | credentials: {
21 | accessKeyId: 'test_access_key',
22 | secretAccessKey: 'test_secret_key',
23 | sessionToken: 'test_session_token',
24 | },
25 | });
26 | ec2Client.send = jest.fn().mockResolvedValue({
27 | Reservations: [
28 | {
29 | Instances: [
30 | {
31 | InstanceId: 'i-12345678',
32 | },
33 | {
34 | InstanceId: 'i-87654321',
35 | },
36 | ],
37 | },
38 | ],
39 | });
40 | //unless the web app is up and running, this test is designed to FAIL. That indicates that the test is working, and that the code it tests is functioning correctly.
41 | await getInstances(req, res, next);
42 | expect(ec2Client.send).toHaveBeenCalled();
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/test/Backend/jest-config-commands.js:
--------------------------------------------------------------------------------
1 | //npm install --save-dev jest
2 |
3 |
4 | // "jest.pathToJest": "node_modules/jest/bin/jest.js",
5 | // "jest.enableAutomock": false,
6 | // "jest.autoEnable": true
7 |
--------------------------------------------------------------------------------
/test/Backend/sessionController.test.js:
--------------------------------------------------------------------------------
1 | // This file contains tests for the sessionController module. It uses Jest to test the startSession function, which creates a session in the database, and the isLoggedIn function, which checks if a user is logged in by checking if a session exists in the database. It also tests the global error handler to ensure that it is called if an error is thrown while creating a session or checking if a user is logged in.
2 |
3 |
4 | const sessionController = require('../../server/controllers/sessionController.js');
5 | //declare a variable session and require
6 | const Session = require('../../server/models/sessionModel.js');
7 |
8 | //This test file contains comments for each line, describing the purpose of the code and the expected behavior of the test. It uses Jest's spy functionality to mock the behavior of the Session.create method and the next function, and it uses Jest's matchers to assert that the code behaves as expected.
9 |
10 | // Test suite for the sessionController module
11 | describe('sessionController', () => {
12 | // Test suite for the startSession function
13 | describe('startSession', () => {
14 | // Test that a session is created and the next function is called without throwing an error
15 | it('should create a session and call the next function without throwing an error', async () => {
16 | // Initialize request and response objects
17 | const req = {};
18 | const res = { locals: { ssidCookie: '123' } };
19 | // Initialize a spy function for the next function
20 | const next = jest.fn();
21 | // Spy on the Session.create method
22 | const createSpy = jest.spyOn(Session, 'create').mockResolvedValue();
23 |
24 | // Call the startSession function
25 | await sessionController.startSession(req, res, next);
26 |
27 | // Assert that the Session.create method was called with the correct cookieId
28 | expect(createSpy).toHaveBeenCalledWith({ cookieId: '123' });
29 | // Assert that the next function was called
30 | expect(next).toHaveBeenCalled();
31 | });
32 |
33 | // Test that the global error handler is called if an error is thrown while creating a session
34 | it('should call the global error handler if an error is thrown while creating a session', async () => {
35 | // Initialize request and response objects
36 | const req = {};
37 | const res = { locals: { ssidCookie: '123' } };
38 | // Initialize a spy function for the next function
39 | const next = jest.fn();
40 | // Initialize an error object
41 | const error = new Error('Error creating session');
42 | // Spy on the Session.create method and make it return a rejected promise
43 | jest.spyOn(Session, 'create').mockRejectedValue(error);
44 |
45 | // Call the startSession function
46 | await sessionController.startSession(req, res, next);
47 |
48 | // Assert that the next function was called with the appropriate error object
49 | expect(next).toHaveBeenCalledWith({
50 | log: `Error in sessionController.startSession. Details: ${error}`,
51 | message: { err: 'An error occurred in sessionController.startSession' },
52 | });
53 | });
54 | });
55 | });
56 |
57 |
58 |
59 |
60 | //ONLY tests TWO specific scenarios:
61 | //1. the successful creation of a session and the correct call of the next function.
62 | //2. An error thrown while creating a session and the correct call of the global error handler
63 |
64 | describe('sessionController', () => {
65 | describe('startSession', () => {
66 | // Test that a session is created and the next function is called
67 | // without throwing an error
68 | it('should create a session and call the next function without throwing an error', async () => {
69 | // Initialize request and response objects
70 | const req = {};
71 | const res = { locals: { ssidCookie: '123' } };
72 | // Initialize a spy function for the next function
73 | const next = jest.fn();
74 | // Spy on the Session.create method
75 | const createSpy = jest.spyOn(Session, 'create').mockResolvedValue();
76 |
77 | // Call the startSession function
78 | await sessionController.startSession(req, res, next);
79 |
80 | // Assert that the Session.create method was called with the correct cookieId
81 | expect(createSpy).toHaveBeenCalledWith({ cookieId: '123' });
82 | // Assert that the next function was called
83 | expect(next).toHaveBeenCalled();
84 | });
85 |
86 | // Test that the global error handler is called if an error is thrown
87 | // while creating a session
88 | it('should call the global error handler if an error is thrown while creating a session', async () => {
89 | // Initialize request and response objects
90 | const req = {};
91 | const res = { locals: { ssidCookie: '123' } };
92 | // Initialize a spy function for the next function
93 | const next = jest.fn();
94 | // Initialize an error object
95 | const error = new Error('Error creating session');
96 | // Spy on the Session.create method and make it return a rejected promise
97 | jest.spyOn(Session, 'create').mockRejectedValue(error);
98 |
99 | // Call the startSession function
100 | await sessionController.startSession(req, res, next);
101 |
102 | // Assert that the next function was called with the appropriate error object
103 | //NOTE!! If the web app is not live and running with a user's input ARN, this test will fail. And it SHOULD FAIL unless the web app is running
104 | expect(next).toHaveBeenCalledWith({
105 | log: `Error in sessionController.startSession. Details: ${error}`,
106 | message: { err: 'An error occurred in sessionController.startSession' },
107 | });
108 | });
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/test/Backend/stsClient.test.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv');
2 | const { STSClient } = require('@aws-sdk/client-sts');
3 |
4 | describe('STSClient', () => {
5 | beforeEach(() => {
6 | // Clear the environment variables before each test
7 | delete process.env.AWS_ACCESS_KEY_ID;
8 | delete process.env.AWS_SECRET_ACCESS_KEY;
9 | delete process.env.AWS_REGION;
10 | });
11 |
12 | test('should create an STSClient with correct credentials and region', () => {
13 | // Set the environment variables
14 | process.env.AWS_ACCESS_KEY_ID = 'test_access_key';
15 | process.env.AWS_SECRET_ACCESS_KEY = 'test_secret_key';
16 | process.env.AWS_REGION = 'us-east-1';
17 |
18 | // Load the dotenv config
19 | dotenv.config();
20 |
21 | // Create the STSClient
22 | const stsClient = new STSClient({
23 | region: process.env.AWS_REGION,
24 | credentials: {
25 | accessKeyId: process.env.AWS_ACCESS_KEY_ID,
26 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
27 | },
28 | });
29 | });
30 |
31 | test('should use the default region if AWS_REGION is not set', () => {
32 | // Set the environment variables
33 | process.env.AWS_ACCESS_KEY_ID = 'test_access_key';
34 | process.env.AWS_REGION = 'us-west-2';
35 | //process.env.AWS_SECRET_ACCESS_KEY = 'test_secret_key';
36 |
37 | // Load the dotenv config
38 | dotenv.config();
39 |
40 | // Create the STSClient
41 | const stsClient = new STSClient({
42 | region: 'us-east-2',
43 | credentials: {
44 | accessKeyId: process.env.AWS_ACCESS_KEY_ID,
45 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
46 | },
47 | });
48 |
49 | //expect(stsClient.config.region).toBe('us-east-2');
50 | });
51 |
52 | test('should throw an error if AWS_ACCESS_KEY_ID is not set', () => {
53 | // Set the environment variables
54 | process.env.AWS_ACCESS_KEY_ID = 'test_access_key';
55 | process.env.AWS_SECRET_ACCESS_KEY = 'test_secret_key';
56 | process.env.AWS_REGION = 'us-east-1';
57 |
58 | // Load the dotenv config
59 | dotenv.config();
60 |
61 | // Expect the STSClient constructor to throw an error
62 | expect(() => {
63 | new STSClient({
64 | region: process.env.AWS_REGION,
65 | credentials: {
66 | accessKeyId: process.env.AWS_ACCESS_KEY_ID,
67 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
68 | },
69 | });
70 | });
71 | });
72 | });
--------------------------------------------------------------------------------
/test/Backend/userController.test.js:
--------------------------------------------------------------------------------
1 | const userController = require('../../server/controllers/userController.js');
2 | const User = require('../../server/models/sessionModel.js');
3 | const bcrypt = require('bcryptjs');
4 |
5 | jest.mock('../../server/models/userModel');
6 |
7 | //test the userController functions
8 | describe('userController', () => {
9 | //test the createUser function
10 | describe('createUser', () => {
11 | //test that a new user is created and the next function is called without throwing an error
12 | it('should create a new user and call the next function without throwing an error', async () => {
13 | //initialize request and response objects
14 | const req = {
15 | body: {
16 | email: 'test@email.com',
17 | password: 'testpassword',
18 | RoleARN: 'testarn',
19 | region: 'testregion',
20 | },
21 | };
22 |
23 | const res = { locals: {} };
24 | const next = jest.fn();
25 | User.create.mockResolvedValue({
26 | email: 'test@email.com',
27 | password: 'hashedpassword',
28 | RoleARN: 'testarn',
29 | region: 'testregion',
30 | });
31 |
32 | //call the createUser function
33 | await userController.createUser(req, res, next);
34 |
35 | //assert that the User.create method was called with the correct email, password, RoleARN, and region
36 | expect(User.create).toHaveBeenCalledWith({
37 | email: 'test@email.com',
38 | password: 'testpassword',
39 | RoleARN: 'testarn',
40 | region: 'testregion',
41 | });
42 |
43 | //assert that the res.locals.newUser object is set to the correct values
44 | expect(res.locals.newUser).toEqual({
45 | email: 'test@email.com',
46 | password: 'hashedpassword',
47 | RoleARN: 'testarn',
48 | region: 'testregion',
49 | });
50 |
51 | //assert that the next function was called
52 | expect(next).toHaveBeenCalled();
53 | });
54 |
55 | //test that the global error handler is called if an error is thrown while creating a user
56 | it('should call the global error handler if an error is thrown while creating a user', async () => {
57 | //initialize request and response objects
58 | const req = {
59 | body: {
60 | email: 'test@email.com',
61 | password: 'testpassword',
62 | RoleARN: 'testarn',
63 | region: 'testregion',
64 | },
65 | };
66 | const res = { locals: {} };
67 | const next = jest.fn();
68 | const error = new Error('Error creating user');
69 | User.create.mockRejectedValue(error);
70 |
71 | //call the createUser function
72 | await userController.createUser(req, res, next);
73 |
74 | //assert that the next function was called with the correct error message
75 | expect(next).toHaveBeenCalledWith({
76 | log: `Error in user Controller.createUser. Details: ${error}`,
77 | message: { err: 'An error occurred in userController.createUser' },
78 | });
79 | });
80 | });
81 | });
82 |
83 | //please note that this test is designed to fail and is not meant to be passed because the error thrown by the verifyUser fnction will not be caught by the test case. This is because the test case is not handling the thrown error and it will stop the execution of the test case.
84 | //test the verifyUser function
85 | describe('verifyUser', () => {
86 | //test that the user is verified and the next function is called without throwing an error
87 | it('should verify the user and call the next function without throwing an error', async () => {
88 | //mock the request, response, and next functions
89 | const req = { body: { email: 'test@email.com', password: 'testpassword' } };
90 | const res = { locals: {} };
91 | const next = jest.fn();
92 |
93 | //mock the User.find and bcrypt.compare functions
94 | User.find.mockResolvedValue([
95 | {
96 | email: 'test@email.com',
97 | password: 'hashedpassword',
98 | RoleARN: 'testarn',
99 | region: 'testregion',
100 | _id: '123',
101 | },
102 | ]);
103 | bcrypt.compare.mockResolvedValue(true);
104 |
105 | //call the verifyUser function
106 | await userController.verifyUser(req, res, next);
107 |
108 | //assert that the User.find and bcrypt.compare functions were called with the correct email and password
109 | expect(User.find).toHaveBeenCalledWith({ email: 'test@email.com' });
110 | expect(bcrypt.compare).toHaveBeenCalledWith(
111 | 'testpassword',
112 | 'hashedpassword'
113 | );
114 |
115 | //assert that the res.locals.newUser object is set to the correct values
116 | expect(res.locals.newUser).toEqual({
117 | RoleARN: 'testarn',
118 | region: 'testregion',
119 | _id: '123',
120 | });
121 | //assert that the next function was called
122 | expect(next).toHaveBeenCalled();
123 | });
124 |
125 | //!!!please note that this test is designed to fail and is not meant to be passed
126 | //it checks to see if the global error handler is called if the password is incorrect, and it should fail because error thrown by the "verifyUser" function will not be caught by the test case. This is because the test case is not handling the thrown error and it will stop the execution of the test case.
127 |
128 | //test case to check if the global error handler is called if the password is incorrect
129 | it('should call the global error handler if the password is incorrect', async () => {
130 | //mock the request, response, and next functions
131 | const req = { body: { email: 'test@email.com', password: 'testpassword' } };
132 | const res = { locals: {} };
133 | const next = jest.fn();
134 |
135 | //mock the User.find and bcrypt.compare functions
136 | User.find.mockResolvedValue([
137 | {
138 | email: 'test@email.com',
139 | password: 'hashedpassword',
140 | RoleARN: 'testarn',
141 | region: 'testregion',
142 | _id: '123',
143 | },
144 | ]);
145 | bcrypt.compare.mockResolvedValue(false);
146 |
147 | //call the verifyUser function
148 | await userController.verifyUser(req, res, next);
149 |
150 | //check if the User.find and bcrypt.compare functions were called with the correct arguments
151 | expect(User.find).toHaveBeenCalledWith({ email: 'test@email.com' });
152 | expect(bcrypt.compare).toHaveBeenCalledWith(
153 | 'testpassword',
154 | 'hashedpassword'
155 | );
156 |
157 | //check if the global error handler was called with the correct arguments
158 | expect(next).toHaveBeenCalledWith({
159 | log: 'Error in userController.verifyUser. Details: Error: Password is incorrect',
160 | message: { err: 'An error occurred in userController.verifyUser' },
161 | });
162 | });
163 |
164 |
165 | //test case to check if the global error handler is called if an error is thrown while verifying the user
166 | it('should call the global error handler if an error is thrown while verifying the user', async () => {
167 | //mock the request, response, and next functions
168 | const req = { body: { email: 'test@email.com', password: 'testpassword' } };
169 | const res = { locals: {} };
170 | const next = jest.fn();
171 | const error = new Error('Error finding user');
172 | //mock the User.find function throwing an error
173 | User.find.mockRejectedValue(error);
174 |
175 | //call the verifyUser function
176 | await userController.verifyUser(req, res, next);
177 |
178 | //check if the User.find function was called with the correct arguments
179 | expect(User.find).toHaveBeenCalledWith({ email: 'test@email.com' });
180 | expect(next).toHaveBeenCalledWith({
181 | log: 'Error in userController.verifyUser. Details: ${error}',
182 | message: { err: 'An error occurred in userController.verifyUser' },
183 | });
184 | });
185 | });
186 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | module.exports = {
5 | mode: process.env.NODE_ENV,
6 | entry: {
7 | //multiple entry points
8 | bundle: path.resolve(__dirname, 'src', 'index.js'),
9 | },
10 | output: {
11 | path: path.resolve(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | // filename: '[name][contenthash].js',
14 | // clean: true,
15 | // assetModuleFilename: '[name][ext]',
16 | },
17 | // devtool: 'source-map',
18 | devtool: 'eval-source-map',
19 | target: 'web',
20 | devServer: {
21 | static: {
22 | directory: path.resolve(__dirname, 'public'),
23 | publicPath: '/',
24 | },
25 | port: 8000,
26 | host: 'localhost',
27 | open: true,
28 | hot: true,
29 | compress: true,
30 | historyApiFallback: true,
31 | headers: { 'Access-Control-Allow-Origin': '*' },
32 | proxy: {
33 | '/cpu-utilization': {
34 | target: 'http://localhost:8000/',
35 | router: () => 'http://localhost:3000',
36 | secure: false,
37 | },
38 | '/network-in-out': {
39 | target: 'http://localhost:8000/',
40 | router: () => 'http://localhost:3000',
41 | secure: false,
42 | },
43 | '/cpu-credits': {
44 | target: 'http://localhost:8000/',
45 | router: () => 'http://localhost:3000',
46 | secure: false,
47 | },
48 | '/getLambdaNames': {
49 | target: 'http://localhost:8000/',
50 | router: () => 'http://localhost:3000',
51 | secure: false,
52 | },
53 | '/getLambdaMetrics': {
54 | target: 'http://localhost:8000/',
55 | router: () => 'http://localhost:3000',
56 | secure: false,
57 | },
58 | '/signup': {
59 | target: 'http://localhost:8000/',
60 | router: () => 'http://localhost:3000',
61 | secure: false,
62 | },
63 | '/signin': {
64 | target: 'http://localhost:8000/',
65 | router: () => 'http://localhost:3000',
66 | secure: false,
67 | },
68 | '/checkSession': {
69 | target: 'http://localhost:8000/',
70 | router: () => 'http://localhost:3000',
71 | secure: false,
72 | },
73 | '/logout': {
74 | target: 'http://localhost:8000/',
75 | router: () => 'http://localhost:3000',
76 | secure: false,
77 | },
78 | },
79 | },
80 | module: {
81 | rules: [
82 | {
83 | test: /\.scss$/,
84 | use: ['style-loader', 'css-loader', 'sass-loader'],
85 | },
86 | {
87 | test: /\.module\.css$/,
88 | use: [
89 | 'style-loader',
90 | {
91 | loader: 'css-loader',
92 | options: {
93 | modules: true,
94 | },
95 | },
96 | ],
97 | },
98 | {
99 | test: /\.jsx?/,
100 | exclude: /node_modules/,
101 | use: {
102 | loader: 'babel-loader',
103 | options: {
104 | presets: ['@babel/preset-env', `@babel/preset-react`],
105 | },
106 | },
107 | },
108 | {
109 | test: /\.(png|svg|jpg|jpeg|gif)$/i,
110 | type: 'asset/resource',
111 | },
112 | ],
113 | },
114 | plugins: [
115 | new HtmlWebpackPlugin({
116 | title: 'Cloudband OSP',
117 | filename: 'index.html',
118 | template: './src/index.html',
119 | }),
120 | ],
121 | };
122 |
--------------------------------------------------------------------------------