├── .babelrc
├── .dockerignore
├── .gitignore
├── App.tsx
├── Dockerfile
├── LICENSE
├── README.md
├── __tests__
└── Example.js
├── client
├── GraphData.ts
├── assets
│ ├── PulsarLogoSVG.svg
│ ├── networkScreenshot.png
│ ├── ppdemogif.gif
│ ├── pulsarLogo.png
│ └── space.jpg
├── components
│ ├── Display.tsx
│ ├── Graph.tsx
│ ├── GraphMenu.tsx
│ └── Navbar.tsx
├── index.html
├── styles
│ └── styles.scss
└── types.ts
├── compose.yml
├── imageConfigs
├── grafana
│ ├── Dockerfile
│ ├── grafana.ini
│ └── provisioning
│ │ ├── dashboards
│ │ ├── backlog.json
│ │ ├── countGauge.json
│ │ ├── dashboard.yml
│ │ ├── jetty.json
│ │ ├── messageRateIn.json
│ │ ├── messageRateOut.json
│ │ ├── messagesin.json
│ │ ├── messagesout.json
│ │ ├── throughputin.json
│ │ ├── throughputout.json
│ │ ├── topicsandsubscriptions.json
│ │ └── usageGauge.json
│ │ └── datasources
│ │ └── srcProm.yml
└── prometheus
│ ├── Dockerfile
│ └── config
│ └── prometheus.yml
├── index.html
├── index.tsx
├── package-lock.json
├── package.json
├── server
└── server.ts
├── tsconfig.json
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react", "@babel/preset-env", "@babel/preset-typescript"],
3 | "env": {
4 | "test": {
5 | "plugins": ["@babel/plugin-transform-modules-commonjs"]
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /imageConfigs/grafana/data
3 | /imageConfigs/prometheus/promData
4 | oggrafdata/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .vscode
4 | /imageConfigs/grafana/data
5 | /imageConfigs/prometheus/promData
6 | oggrafdata/
--------------------------------------------------------------------------------
/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Display from './client/components/Display';
3 | import Navbar from './client/components/Navbar';
4 |
5 | const App = () => {
6 | return (
7 | <>
8 |
PulsarPortrait
9 |
10 |
11 | {' '}
12 | {' '}
13 |
14 |
15 | {' '}
16 | {' '}
17 |
18 |
19 | >
20 | );
21 | };
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:latest
2 |
3 | WORKDIR /app
4 |
5 | COPY package*.json ./
6 |
7 | RUN npm install
8 |
9 | COPY . .
10 |
11 | RUN npm run build
12 |
13 | ENV PORT=3333
14 |
15 | EXPOSE 3333
16 |
17 | ENTRYPOINT ["node", "./server/server.ts"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 OSLabs Beta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # PulsarPortrait
6 | https://pulsarportrait.io/
7 |
8 | ## About
9 |
10 |
11 |
12 | 
13 | 
14 | 
15 | 
16 | 
17 | 
18 | 
19 | 
20 | 
21 | 
22 | 
23 | 
24 | 
25 |
26 |
27 |
28 | PulsarPortrait is an open-source application that illustrates the current health of your Pulsar cluster. By scraping data from Prometheus metrics, the app can display a variety of graphs, gauges, and counters. From message data to jetty response times, users can choose from eleven visualizations and frame them in any of the six display slots.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## Getting Started
37 |
38 | ### Option 1 - Add PulsarPortrait to your compose.yml file
39 |
40 | Simply copy and paste the following code into the services dictionary of your compose.yml file. Please note that if your network is not pulsar, you will need to change it in the networks section of each service.
41 |
42 | ```yml
43 | prometheus:
44 | image: pulsarportrait/prometheus:latest
45 | container_name: prometheus
46 | networks:
47 | - pulsar
48 | ports:
49 | - '9090:9090'
50 |
51 | pulsarportrait:
52 | image: pulsarportrait/app:latest
53 | container_name: pulsarportrait
54 | networks:
55 | - pulsar
56 | ports:
57 | - '3333:3333'
58 |
59 | grafana:
60 | image: pulsarportrait/grafana:latest
61 | container_name: grafana
62 | networks:
63 | - pulsar
64 | ports:
65 | - '2222:3000'
66 | depends_on:
67 | - prometheus
68 | ```
69 |
70 | ### Option 2 - Use this repo
71 |
72 | if you already have a project with Pulsar and do not wish to edit the compose.yml file, you can follow these steps:
73 |
74 | - Fork and clone this repo
75 | - Start your Pulsar culster
76 | - In this repo, add your Pulsar cluster's network to the compose.yml file, if you are unsure of the name of your network, you can run docker network ls
77 |
78 |
79 |
80 | - Lastly, run docker compose up -d, and PulsarPortrait will be available on http://localhost:3333/
81 |
82 |
83 |
84 | ## Contributions
85 | We welcome feedback and contributions to this project. If you want to add a feature or fix a bug, please follow these steps:
86 | - Fork and clone this repo
87 | - Create your feature branch by running
88 | ```zsh
89 | git checkout -b your-feature-branch-name
90 | ```
91 | - Add, commit, and push your changes up to GitHub
92 | - Lastly, make a pull request detailing your changes
93 |
94 |
95 |
96 | ## Authors
97 |
98 | - Grant Thomas [GitHub](https://github.com/GrantCT) | [LinkedIn](https://www.linkedin.com/in/grantcthomas/)
99 | - Cyrux Lam [GitHub](https://github.com/cyduckk) | [LinkedIn](https://www.linkedin.com/in/cyrux-lam/)
100 | - Anthony Le [GitHub](https://github.com/anthonyle910) | [LinkedIn](https://www.linkedin.com/in/anthony-le-616b4b101/)
101 | - Jordan Zolman [Github](https://github.com/PrincePuggo) | [LinkedIn](https://www.linkedin.com/in/jordanzolman)
102 |
103 |
104 |
105 | ## License
106 | - PulsarPortrait is MIT Licensed
--------------------------------------------------------------------------------
/__tests__/Example.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "@testing-library/jest-dom";
3 | import { render, screen } from "@testing-library/react";
4 | import {describe, expect, test} from '@jest/globals'
5 | import Example from '../client/components/Example'
6 |
7 | describe("example", () => {
8 | it("should have the text example", () => {
9 | render(
10 |
11 |
12 |
13 | );
14 | expect(screen.getByTestId('testid')).toHaveTextContent('example');
15 | });
16 | });
17 |
18 | describe('sum', () => {
19 | test('adds 1 + 2 to equal 3', () => {
20 | expect(1 + 2).toBe(3);
21 | });
22 | });
--------------------------------------------------------------------------------
/client/GraphData.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | messagesIn:
3 | 'http://localhost:2222/d/c2ef0e4a-da28-4797-bd86-5c1957aa9039/newmessagesin?orgId=1&refresh=5s&viewPanel=1&viewPanel=1&kiosk',
4 | messagesOut:
5 | 'http://localhost:2222/d/b7cc76a7-51f0-4175-bbbc-dada7ec3fa2d/messagesout?orgId=1&refresh=5s&viewPanel=1&kiosk',
6 | backlog:
7 | 'http://localhost:2222/d/d20d409b-6974-4ca4-b16c-fc74c6aa7cd1/newbacklog?orgId=1&refresh=5s&viewPanel=1&kiosk',
8 | topicsAndSubscriptions:
9 | 'http://localhost:2222/d/c8268214-d094-46d5-9a25-7cce1192ae69/topicsandsubscriptions?orgId=1&refresh=5s&viewPanel=1&kiosk',
10 | memoryUsage:
11 | 'http://localhost:2222/d/a6f621d3-bb3c-4569-8315-2e0c4b2de35b/new-dashboard?orgId=1&refresh=5s&viewPanel=1&kiosk',
12 | activeConnections:
13 | 'http://localhost:2222/d/d79b197c-2abe-4e1b-b16e-0fa10b0656a4/test?orgId=1&refresh=5s&viewPanel=1&kiosk',
14 | jettyrequesttime:
15 | 'http://localhost:2222/d/e11221ec-7d82-47ec-a3b1-5356e4f63c6b/jettyrequesttime?orgId=1&refresh=5s&viewPanel=1&kiosk',
16 | throughputIn:
17 | 'http://localhost:2222/d/bdfb696f-55e4-46da-aa3d-381b5cf2f408/throughputin?orgId=1&refresh=5s&viewPanel=1&kiosk',
18 | throughputOut:
19 | 'http://localhost:2222/d/b1f5eb24-9c75-4ba3-a40b-7bf8e1ccdc94/throughtputout?orgId=1&refresh=5s&viewPanel=1&kiosk',
20 | messageRateIn:
21 | 'http://localhost:2222/d/a9961e9a-b44a-42c1-a855-427e3825b04c/broker-rate-in?orgId=1&refresh=5s&viewPanel=1&kiosk',
22 | messageRateOut:
23 | 'http://localhost:2222/d/b9031b09-082f-4dc6-9b82-766c5e543328/message-rate-out?orgId=1&refresh=5s&viewPanel=1&kiosk',
24 | };
25 |
26 |
--------------------------------------------------------------------------------
/client/assets/networkScreenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/PulsarPortrait/9c89aa552eb662569dd5d401843c0b1f8d70163c/client/assets/networkScreenshot.png
--------------------------------------------------------------------------------
/client/assets/ppdemogif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/PulsarPortrait/9c89aa552eb662569dd5d401843c0b1f8d70163c/client/assets/ppdemogif.gif
--------------------------------------------------------------------------------
/client/assets/pulsarLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/PulsarPortrait/9c89aa552eb662569dd5d401843c0b1f8d70163c/client/assets/pulsarLogo.png
--------------------------------------------------------------------------------
/client/components/Display.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Graph from './Graph'
3 |
4 | // move graph data to graph component, set an array of 6 graph data key's and assign that index to the key value of each graph being pushed. set initial state
5 | // to be equal to a position in that array. Have graph render new URL based on setUrl setState defined in graphMenu.
6 |
7 | export default function Display() {
8 |
9 | const graphArr: React.JSX.Element[] = [];
10 |
11 | for (let i = 0; i < 6; i++) {
12 | graphArr.push( )
13 | }
14 |
15 | return (
16 |
17 | {graphArr}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/client/components/Graph.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import GraphMenu from './GraphMenu';
3 | import { useState } from 'react';
4 | import GraphData from '../GraphData';
5 | import { URLS } from '../types';
6 |
7 | type GraphProps = {
8 | index: number;
9 | }
10 |
11 | export default function Graph({ index }: GraphProps) {
12 | const data: URLS = GraphData;
13 |
14 | const graphDataArray: string[] = [
15 | data['messagesIn'],
16 | data['messagesOut'],
17 | data['backlog'],
18 | data['activeConnections'],
19 | data['memoryUsage'],
20 | data['topicsAndSubscriptions'],
21 | ];
22 | const graphNameArray: string[] = [
23 | 'Messages In',
24 | 'Messages Out',
25 | 'Backlog',
26 | 'Producer/Consumer Count',
27 | 'Memory Usage',
28 | 'Topics and Subscriptions',
29 | ]
30 |
31 | const [name, setName] = useState(graphNameArray[index]);
32 | const [url, setUrl] = useState(graphDataArray[index]);
33 |
34 | return (
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/client/components/GraphMenu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Button from '@mui/material/Button';
3 | import Menu from '@mui/material/Menu';
4 | import MenuItem from '@mui/material/MenuItem';
5 | import Fade from '@mui/material/Fade';
6 | import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
7 | import GraphData from '../GraphData';
8 | import { useState } from 'react';
9 | import { URLS } from '../types';
10 |
11 |
12 | interface GraphMenuProps {
13 | name: string;
14 | setName: Function;
15 | setUrl: Function
16 | }
17 |
18 | export default function GraphMenu({ name, setName, setUrl}: GraphMenuProps) {
19 |
20 | const data: URLS = GraphData//GraphData();
21 |
22 | const [anchorEl, setAnchorEl] = React.useState(null);
23 | const open: boolean = Boolean(anchorEl);
24 |
25 | const handleClick = (event: React.MouseEvent): void => {
26 | setAnchorEl(event.currentTarget);
27 | };
28 |
29 | const handleClose = (): void => {
30 | setAnchorEl(null);
31 | };
32 |
33 | const changeGraph = (e: React.BaseSyntheticEvent) => {
34 | const { id } = e.target;
35 | let temp;
36 |
37 | switch(id) {
38 | case data['activeConnections']: temp = 'active Connections';
39 | break;
40 | case data['backlog']: temp = 'backlog';
41 | break;
42 | case data['jettyrequesttime']: temp = 'jetty request time';
43 | break;
44 | case data['memoryUsage']: temp = 'memory Usage';
45 | break;
46 | case data['messagesIn']: temp = 'messages In';
47 | break;
48 | case data['messagesOut']: temp = 'messages Out';
49 | break;
50 | case data['messageRateIn']: temp = 'message Rate In';
51 | break;
52 | case data['messageRateOut']: temp = 'message Rate Out';
53 | break;
54 | case data['throughputIn']: temp = 'throughput In';
55 | break;
56 | case data['throughputOut']: temp = 'throughput Out';
57 | break;
58 | case data['topicsAndSubscriptions']: temp = 'topics And Subscriptions';
59 | break;
60 | }
61 |
62 | setUrl(id);
63 | setName(temp);
64 | setAnchorEl(null);
65 | };
66 |
67 | return (
68 |
69 | }
76 | sx={{ fontSize: '10px' }}
77 | >
78 | {name}
79 |
80 |
130 |
131 | );
132 | }
133 |
--------------------------------------------------------------------------------
/client/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import HomeIcon from '@mui/icons-material/Home';
3 | import SettingsIcon from '@mui/icons-material/Settings';
4 | import InfoIcon from '@mui/icons-material/Info';
5 | import { Button } from '@mui/material';
6 |
7 | export default function Navbar() {
8 | return (
9 |
10 | } sx={{mt:'12px',width: '80%', fontSize: '.7em'}}>Home
11 | } sx={{mt: '12px',width: '80%', fontSize: '.5em'}}>Settings
12 | } sx={{mt: '12px', width: '80%', fontSize: '.5em'}}>About
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Pulsar Portrait
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/client/styles/styles.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Anonymous+Pro&family=Courier+Prime&family=Geologica:wght@300&family=Manrope:wght@300&family=Nunito+Sans&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap');
3 | @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Russo+One&display=swap');
4 |
5 | * {
6 | margin: 0;
7 | padding: 0;
8 | box-sizing: border-box;
9 | }
10 |
11 | #heading {
12 | color: white;
13 | text-align: center;
14 | margin-bottom: 0px;
15 | font-size: 50pt;
16 | }
17 |
18 | html, body {
19 | height: 100%;
20 | }
21 |
22 | body {
23 | background-color: grey;
24 | font-family: 'Russo One', sans-serif;
25 | // background-image: url("https://steamuserimages-a.akamaihd.net/ugc/1678120729579339650/40A46978EF29C7099912D8CA459BBC9FAC764F2E/?imw=5000&imh=5000&ima=fit&impolicy=Letterbox&imcolor=#000000&letterbox=false");
26 | // background-image: url("https://media.discordapp.net/attachments/1120822627880144916/1126580577835954176/puggo_blue_and_purple_colored_background_with_purple_and_blue_l_0b542b64-a0ac-4990-8ccb-e3d89df2bc6a.png");
27 | // background-repeat: no-repeat;
28 | background-size: cover;
29 | // background-position: 50% 50%;
30 | // image-rendering: pixelated;
31 | // background-size: cover;
32 | // height: 100vh;
33 | // width: 100vw;
34 |
35 | display: flex;
36 | flex-direction: column;
37 |
38 | // background-image: url("https://media.discordapp.net/attachments/1086022677422153820/1125576700042948709/puggo_an_image_in_the_style_of_a_graphic_designer_of_four_infor_1227d67b-eec8-460a-8d0e-35c0a7c029bc.png?width=1138&height=1138");
39 | }
40 |
41 | #root {
42 | margin-top: 0px;
43 | display: flex;
44 | flex-direction: column;
45 | // justify-content: center;
46 | align-items: center;
47 | height: 100%;
48 | // min-height: 100%;
49 | // border: 1px solid pink;
50 | }
51 | .app-display {
52 | // margin: 2.5rem;
53 | // border: 1px solid blue;
54 | display: flex;
55 | flex-direction: row;
56 | flex-grow: 1;
57 | // border: 1px solid black;
58 | // min-height: 90vh;
59 | // width: 95vw;
60 | width: 95%;
61 | // height: 95%;
62 | border-radius: 5px;
63 | // margin-bottom: 20px;
64 | }
65 |
66 | .navbar {
67 | display: flex;
68 | justify-content: center;
69 | // border: 1px solid green;
70 | background-color: purple;
71 | border-radius: 5px;
72 | // width: 5rem;
73 | .nav-buttons {
74 | color: white;
75 | padding: 5px;
76 | margin-bottom: 10px;
77 | margin-top: 5px;
78 | }
79 | .nav-buttons:hover {
80 | background-color: rgba(158, 98, 190, 0.686);
81 | cursor: pointer;
82 | }
83 | // svg:hover {
84 | // width: 2.5rem;
85 | // cursor: pointer;
86 | // border-radius: 6px;
87 | // }
88 | // svg:hover {
89 | // width: 2.5rem;
90 | // cursor: pointer;
91 | // border-radius: 6px;
92 | // }
93 | }
94 |
95 | .body {
96 | // display: flex;
97 | // flex-direction: row;
98 | // border: 1px solid blue;
99 | // width: 99vw;
100 | // background-color: rgb(60, 60, 60);
101 | width: 100%;
102 | height: 100%;
103 | background: linear-gradient(91.7deg, rgb(50, 25, 79) -4.3%, rgb(50, 25, 83) 101.8%);
104 | border-top-right-radius: 5px;
105 | border-bottom-right-radius: 5px;
106 | border-radius: 5px;
107 | // border: 1px solid green;
108 |
109 | }
110 |
111 | // .display-container {
112 | // display: flex;
113 | // flex-direction: row;
114 | // flex-wrap: wrap;
115 | // justify-content: space-between;
116 | // // height: 90vh;
117 | // gap: 1rem;
118 | // border-radius: 5px;
119 | // padding-left: 3rem;
120 | // padding-right: 3rem;
121 | // padding-top: 2rem;
122 | // padding-bottom: 1rem;
123 | // // border: 2px solid pink;
124 | // }
125 |
126 | .display-container {
127 | display: grid;
128 | width: 100%;
129 | // flex-direction: row;
130 | // flex-wrap: wrap;
131 | // justify-content: space-between;
132 | // height: 90vh;
133 | // gap: 1rem;
134 | height:100%;
135 |
136 | grid-template-columns: 1fr 1fr;
137 | grid-template-rows: repeat(3, 1fr);
138 |
139 | // grid-template-columns: 1fr 1fr;
140 | // grid-template-rows: repeat(6, 1fr);
141 | border-radius: 5px;
142 | // padding-left: 3rem;
143 | // padding-right: 3rem;
144 | // padding-top: 2rem;
145 | // padding-bottom: 1rem;
146 | // border: 2px solid pink;
147 | }
148 |
149 |
150 |
151 | .graph-container {
152 | // border: 1px solid red;
153 | position: relative;
154 | // padding: 1rem;
155 | padding: .5rem;
156 | // width: 100%;
157 | }
158 | // .Graph {
159 | // // background-color: maroon;
160 | // // display: flex;
161 | // flex-grow: 1;
162 | // border-radius: 5px;
163 | // // display: grid;
164 | // // grid-row: auto;
165 | // color: white;
166 | // // margin: 1rem;
167 | // // height: 8vh;
168 | // // width: 90vw;
169 | // // width: 90%;
170 | // // border: 2px solid white;
171 | // // overflow-y: hidden;
172 | // }
173 |
174 | #home-button {
175 | // height: 200%;
176 | // width: 200%
177 | // size: 35;
178 | color: white;
179 | }
180 |
181 | .navbar-content {
182 | display: flex;
183 | flex-direction: column;
184 | justify-content: flex-start;
185 | align-items: center;
186 | border-top-left-radius: 5px;
187 | border-bottom-left-radius: 5px;
188 | // min-width: 3rem;
189 | // background: radial-gradient(circle at 24.1% 68.8%, rgb(24, 24, 24) 0%, rgb(0, 0, 0) 99.4%);
190 | background: radial-gradient(circle at 15% 25%, rgb(66, 66, 66) 0%, rgb(30, 30, 30) 99.4%);
191 |
192 | // background: linear-gradient(76.8deg, rgb(121, 45, 129) 3.6%, rgb(36, 31, 98) 90.4%);
193 | // height: 100%;
194 | }
195 |
196 | .chart {
197 | // height: 20vh;
198 | // width: 90vw;
199 | // height: 270px;
200 | min-height: 220px;
201 | // width: 55rem;
202 | // min-height: 250px;
203 | height: 100%;
204 | width: 100%;
205 | border-radius: 8px;
206 | overflow: hidden;
207 | }
208 | // @media screen and (max-width: 2012.5px) {
209 | // // .chart {
210 | // // width: 90vw;
211 | // // }
212 | // }
213 |
214 | @media screen and (max-width: 1100px) {
215 | // .chart {
216 | // width: 90vw;
217 | // }
218 | .display-container {
219 | grid-template-columns: 1fr;
220 | grid-template-rows: repeat(6, 1fr);
221 |
222 | }
223 | .app-display {
224 | // height: 100%;
225 | // margin-top: 10px;
226 | min-height: 1600px
227 | }
228 | #root {
229 | // height: 100vh;
230 | // min-height: 2000px;
231 | }
232 |
233 | }
234 |
235 | // @media screen and (min-width: 2000px) {
236 | // .graph-container {
237 | // padding: 20rem;
238 | // background-color: aqua;
239 | // }
240 | // }
241 |
242 |
243 |
244 |
245 |
246 | .graph-container {
247 | // border: 1px solid red;
248 | position: relative;
249 | // padding: 1rem;
250 | padding: .5rem;
251 | // width: 100%;
252 | }
253 | #fade-button {
254 | background-color: rgba(85, 85, 86, 0.775);
255 | color: white
256 | }
257 |
258 | .MuiList-root {
259 | // background-color: rgba(67, 67, 67, 0.775);
260 | // color: white;
261 | padding-left: 20px;
262 | }
263 |
264 | #graph-container1,
265 | #graph-container2 {
266 | position: relative;
267 | }
268 |
269 | .menu {
270 | // top: 5px;
271 | // right: 50px;
272 | position: absolute;
273 |
274 | }
275 |
276 |
--------------------------------------------------------------------------------
/client/types.ts:
--------------------------------------------------------------------------------
1 | export type URLS = {
2 | [key: string]: string
3 | }
--------------------------------------------------------------------------------
/compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | networks:
3 | pulsar-network:
4 | # Write the name of your pulsar-network here
5 | name:
6 | external: true
7 |
8 | services:
9 | prometheus:
10 | image: pulsarportrait/prometheus:latest
11 | container_name: prometheus
12 | networks:
13 | - pulsar-network
14 | ports:
15 | - '9090:9090'
16 | volumes:
17 | - ./imageConfigs/prometheus/promData:/prometheus
18 |
19 | grafana:
20 | image: pulsarportrait/grafana:latest
21 | container_name: grafana
22 | networks:
23 | - pulsar-network
24 | ports:
25 | - "2222:3000"
26 | depends_on:
27 | - prometheus
28 |
29 | pulsarportrait:
30 | image: pulsarportrait/app:latest
31 | container_name: pulsarportrait
32 | networks:
33 | - pulsar-network
34 | ports:
35 | - '3333:3333'
--------------------------------------------------------------------------------
/imageConfigs/grafana/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM grafana/grafana
2 |
3 | COPY ./provisioning/dashboards /etc/grafana/provisioning/dashboards
4 | COPY ./provisioning/datasources /etc/grafana/provisioning/datasources
5 | COPY ./grafana.ini /etc/grafana/grafana.ini
6 |
7 | ENV GF_PATHS_CONFIG='/etc/grafana/grafana.ini'
8 | ENV GF_AUTH_ANONYMOUS_ENABLED='true'
9 |
--------------------------------------------------------------------------------
/imageConfigs/grafana/grafana.ini:
--------------------------------------------------------------------------------
1 | [server]
2 | allow_embedding = true
3 | [security]
4 | allow_embedding = true
5 | [plugins]
6 | plugins = boomtheme-panel
7 | enable_alpha = true
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/backlog.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 12,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "fixedColor": "dark-purple",
34 | "mode": "fixed"
35 | },
36 | "custom": {
37 | "axisCenteredZero": false,
38 | "axisColorMode": "text",
39 | "axisLabel": "",
40 | "axisPlacement": "auto",
41 | "barAlignment": 0,
42 | "drawStyle": "line",
43 | "fillOpacity": 14,
44 | "gradientMode": "opacity",
45 | "hideFrom": {
46 | "legend": false,
47 | "tooltip": false,
48 | "viz": false
49 | },
50 | "lineInterpolation": "smooth",
51 | "lineWidth": 1,
52 | "pointSize": 5,
53 | "scaleDistribution": {
54 | "type": "linear"
55 | },
56 | "showPoints": "auto",
57 | "spanNulls": false,
58 | "stacking": {
59 | "group": "A",
60 | "mode": "none"
61 | },
62 | "thresholdsStyle": {
63 | "mode": "off"
64 | }
65 | },
66 | "mappings": [],
67 | "thresholds": {
68 | "mode": "absolute",
69 | "steps": [
70 | {
71 | "color": "green",
72 | "value": null
73 | },
74 | {
75 | "color": "red",
76 | "value": 80
77 | }
78 | ]
79 | }
80 | },
81 | "overrides": []
82 | },
83 | "gridPos": {
84 | "h": 8,
85 | "w": 12,
86 | "x": 0,
87 | "y": 0
88 | },
89 | "id": 1,
90 | "options": {
91 | "legend": {
92 | "calcs": [],
93 | "displayMode": "list",
94 | "placement": "bottom",
95 | "showLegend": false
96 | },
97 | "tooltip": {
98 | "mode": "single",
99 | "sort": "none"
100 | }
101 | },
102 | "targets": [
103 | {
104 | "datasource": {
105 | "type": "prometheus",
106 | "uid": "PBFA97CFB590B2093"
107 | },
108 | "editorMode": "builder",
109 | "expr": "pulsar_broker_msg_backlog",
110 | "instant": false,
111 | "range": true,
112 | "refId": "A"
113 | }
114 | ],
115 | "title": " ",
116 | "transparent": true,
117 | "type": "timeseries"
118 | }
119 | ],
120 | "refresh": "5s",
121 | "schemaVersion": 38,
122 | "style": "dark",
123 | "tags": [],
124 | "templating": {
125 | "list": []
126 | },
127 | "time": {
128 | "from": "now-5m",
129 | "to": "now"
130 | },
131 | "timepicker": {},
132 | "timezone": "",
133 | "title": "newbacklog",
134 | "uid": "d20d409b-6974-4ca4-b16c-fc74c6aa7cd1",
135 | "version": 9,
136 | "weekStart": ""
137 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/countGauge.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 6,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "description": "",
31 | "fieldConfig": {
32 | "defaults": {
33 | "color": {
34 | "mode": "thresholds"
35 | },
36 | "mappings": [],
37 | "thresholds": {
38 | "mode": "absolute",
39 | "steps": [
40 | {
41 | "color": "dark-purple",
42 | "value": null
43 | }
44 | ]
45 | },
46 | "unit": "short"
47 | },
48 | "overrides": [
49 | {
50 | "matcher": {
51 | "id": "byName",
52 | "options": "pulsar_broker_producers_count"
53 | },
54 | "properties": [
55 | {
56 | "id": "displayName",
57 | "value": "Producers"
58 | }
59 | ]
60 | },
61 | {
62 | "matcher": {
63 | "id": "byName",
64 | "options": "pulsar_broker_consumers_count"
65 | },
66 | "properties": [
67 | {
68 | "id": "displayName",
69 | "value": "Consumers"
70 | }
71 | ]
72 | }
73 | ]
74 | },
75 | "gridPos": {
76 | "h": 8,
77 | "w": 12,
78 | "x": 0,
79 | "y": 0
80 | },
81 | "id": 1,
82 | "options": {
83 | "colorMode": "background",
84 | "graphMode": "none",
85 | "justifyMode": "center",
86 | "orientation": "auto",
87 | "reduceOptions": {
88 | "calcs": ["lastNotNull"],
89 | "fields": "",
90 | "values": false
91 | },
92 | "textMode": "auto"
93 | },
94 | "pluginVersion": "10.0.2",
95 | "targets": [
96 | {
97 | "datasource": {
98 | "type": "prometheus",
99 | "uid": "PBFA97CFB590B2093"
100 | },
101 | "editorMode": "builder",
102 | "expr": "pulsar_broker_producers_count",
103 | "format": "time_series",
104 | "instant": false,
105 | "legendFormat": "{{label_name}}",
106 | "range": true,
107 | "refId": "A"
108 | },
109 | {
110 | "datasource": {
111 | "type": "prometheus",
112 | "uid": "PBFA97CFB590B2093"
113 | },
114 | "editorMode": "builder",
115 | "exemplar": false,
116 | "expr": "pulsar_broker_consumers_count",
117 | "format": "time_series",
118 | "hide": false,
119 | "instant": false,
120 | "interval": "",
121 | "legendFormat": "",
122 | "range": true,
123 | "refId": "B"
124 | }
125 | ],
126 | "title": " ",
127 | "transparent": true,
128 | "type": "stat"
129 | }
130 | ],
131 | "refresh": "5s",
132 | "schemaVersion": 38,
133 | "style": "dark",
134 | "tags": [],
135 | "templating": {
136 | "list": []
137 | },
138 | "time": {
139 | "from": "now-6h",
140 | "to": "now"
141 | },
142 | "timepicker": {},
143 | "timezone": "",
144 | "title": "test",
145 | "uid": "d79b197c-2abe-4e1b-b16e-0fa10b0656a4",
146 | "version": 14,
147 | "weekStart": ""
148 | }
149 |
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/dashboard.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | providers:
4 | - name: 'Prometheus'
5 | orgId: 1
6 | folder: ''
7 | type: file
8 | disableDeletion: false
9 | editable: false
10 | allowUiUpdates: true
11 | options:
12 | path: /etc/grafana/provisioning/dashboards
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/jetty.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 5,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "mappings": [],
33 | "thresholds": {
34 | "mode": "percentage",
35 | "steps": [
36 | {
37 | "color": "green",
38 | "value": null
39 | },
40 | {
41 | "color": "orange",
42 | "value": 70
43 | },
44 | {
45 | "color": "red",
46 | "value": 85
47 | }
48 | ]
49 | },
50 | "unit": "s"
51 | },
52 | "overrides": [
53 | {
54 | "matcher": {
55 | "id": "byName",
56 | "options": "jetty_request_time_seconds_total"
57 | },
58 | "properties": [
59 | {
60 | "id": "thresholds",
61 | "value": {
62 | "mode": "absolute",
63 | "steps": [
64 | {
65 | "color": "green",
66 | "value": null
67 | },
68 | {
69 | "color": "red",
70 | "value": 80
71 | }
72 | ]
73 | }
74 | },
75 | {
76 | "id": "displayName",
77 | "value": "Total"
78 | }
79 | ]
80 | },
81 | {
82 | "matcher": {
83 | "id": "byName",
84 | "options": "jetty_request_time_max_seconds"
85 | },
86 | "properties": [
87 | {
88 | "id": "displayName",
89 | "value": "Max"
90 | }
91 | ]
92 | }
93 | ]
94 | },
95 | "gridPos": {
96 | "h": 6,
97 | "w": 9,
98 | "x": 0,
99 | "y": 0
100 | },
101 | "id": 1,
102 | "options": {
103 | "orientation": "auto",
104 | "reduceOptions": {
105 | "calcs": [
106 | "lastNotNull"
107 | ],
108 | "fields": "",
109 | "values": false
110 | },
111 | "showThresholdLabels": false,
112 | "showThresholdMarkers": true
113 | },
114 | "pluginVersion": "10.0.1",
115 | "targets": [
116 | {
117 | "datasource": {
118 | "type": "prometheus",
119 | "uid": "PBFA97CFB590B2093"
120 | },
121 | "editorMode": "builder",
122 | "expr": "jetty_request_time_max_seconds",
123 | "instant": false,
124 | "range": true,
125 | "refId": "A"
126 | },
127 | {
128 | "datasource": {
129 | "type": "prometheus",
130 | "uid": "PBFA97CFB590B2093"
131 | },
132 | "editorMode": "builder",
133 | "expr": "jetty_request_time_seconds_total",
134 | "hide": false,
135 | "instant": false,
136 | "range": true,
137 | "refId": "B"
138 | }
139 | ],
140 | "title": " ",
141 | "transparent": true,
142 | "type": "gauge"
143 | }
144 | ],
145 | "refresh": "5s",
146 | "schemaVersion": 38,
147 | "style": "dark",
148 | "tags": [],
149 | "templating": {
150 | "list": []
151 | },
152 | "time": {
153 | "from": "now-5m",
154 | "to": "now"
155 | },
156 | "timepicker": {},
157 | "timezone": "",
158 | "title": "jettyrequesttime",
159 | "uid": "e11221ec-7d82-47ec-a3b1-5356e4f63c6b",
160 | "version": 4,
161 | "weekStart": ""
162 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/messageRateIn.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 10,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "description": "",
31 | "fieldConfig": {
32 | "defaults": {
33 | "color": {
34 | "fixedColor": "#cd10b3",
35 | "mode": "fixed"
36 | },
37 | "custom": {
38 | "axisCenteredZero": false,
39 | "axisColorMode": "text",
40 | "axisLabel": "msg/sec",
41 | "axisPlacement": "auto",
42 | "barAlignment": 0,
43 | "drawStyle": "line",
44 | "fillOpacity": 14,
45 | "gradientMode": "none",
46 | "hideFrom": {
47 | "legend": false,
48 | "tooltip": false,
49 | "viz": false
50 | },
51 | "lineInterpolation": "smooth",
52 | "lineWidth": 1,
53 | "pointSize": 5,
54 | "scaleDistribution": {
55 | "type": "linear"
56 | },
57 | "showPoints": "auto",
58 | "spanNulls": false,
59 | "stacking": {
60 | "group": "A",
61 | "mode": "none"
62 | },
63 | "thresholdsStyle": {
64 | "mode": "off"
65 | }
66 | },
67 | "mappings": [],
68 | "thresholds": {
69 | "mode": "absolute",
70 | "steps": [
71 | {
72 | "color": "semi-dark-blue",
73 | "value": null
74 | }
75 | ]
76 | }
77 | },
78 | "overrides": []
79 | },
80 | "gridPos": {
81 | "h": 8,
82 | "w": 12,
83 | "x": 0,
84 | "y": 0
85 | },
86 | "id": 1,
87 | "options": {
88 | "legend": {
89 | "calcs": [],
90 | "displayMode": "list",
91 | "placement": "bottom",
92 | "showLegend": false
93 | },
94 | "tooltip": {
95 | "mode": "single",
96 | "sort": "none"
97 | }
98 | },
99 | "targets": [
100 | {
101 | "datasource": {
102 | "type": "prometheus",
103 | "uid": "PBFA97CFB590B2093"
104 | },
105 | "editorMode": "builder",
106 | "expr": "pulsar_broker_rate_in",
107 | "instant": false,
108 | "range": true,
109 | "refId": "A"
110 | }
111 | ],
112 | "title": " ",
113 | "transparent": true,
114 | "type": "timeseries"
115 | }
116 | ],
117 | "refresh": "5s",
118 | "schemaVersion": 38,
119 | "style": "dark",
120 | "tags": [],
121 | "templating": {
122 | "list": []
123 | },
124 | "time": {
125 | "from": "now-5m",
126 | "to": "now"
127 | },
128 | "timepicker": {},
129 | "timezone": "",
130 | "title": "Broker rate in",
131 | "uid": "a9961e9a-b44a-42c1-a855-427e3825b04c",
132 | "version": 1,
133 | "weekStart": ""
134 | }
135 |
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/messageRateOut.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 10,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "fixedColor": "#b70dc9",
34 | "mode": "fixed"
35 | },
36 | "custom": {
37 | "axisCenteredZero": false,
38 | "axisColorMode": "text",
39 | "axisLabel": "msg/sec",
40 | "axisPlacement": "auto",
41 | "barAlignment": 0,
42 | "drawStyle": "line",
43 | "fillOpacity": 14,
44 | "gradientMode": "none",
45 | "hideFrom": {
46 | "legend": false,
47 | "tooltip": false,
48 | "viz": false
49 | },
50 | "lineInterpolation": "smooth",
51 | "lineWidth": 1,
52 | "pointSize": 5,
53 | "scaleDistribution": {
54 | "type": "linear"
55 | },
56 | "showPoints": "auto",
57 | "spanNulls": false,
58 | "stacking": {
59 | "group": "A",
60 | "mode": "none"
61 | },
62 | "thresholdsStyle": {
63 | "mode": "off"
64 | }
65 | },
66 | "mappings": [],
67 | "thresholds": {
68 | "mode": "absolute",
69 | "steps": [
70 | {
71 | "color": "green",
72 | "value": null
73 | }
74 | ]
75 | }
76 | },
77 | "overrides": []
78 | },
79 | "gridPos": {
80 | "h": 8,
81 | "w": 12,
82 | "x": 0,
83 | "y": 0
84 | },
85 | "id": 1,
86 | "options": {
87 | "legend": {
88 | "calcs": [],
89 | "displayMode": "list",
90 | "placement": "bottom",
91 | "showLegend": false
92 | },
93 | "tooltip": {
94 | "mode": "single",
95 | "sort": "none"
96 | }
97 | },
98 | "targets": [
99 | {
100 | "datasource": {
101 | "type": "prometheus",
102 | "uid": "PBFA97CFB590B2093"
103 | },
104 | "editorMode": "builder",
105 | "expr": "pulsar_broker_rate_out",
106 | "instant": false,
107 | "range": true,
108 | "refId": "A"
109 | }
110 | ],
111 | "title": " ",
112 | "transparent": true,
113 | "type": "timeseries"
114 | }
115 | ],
116 | "refresh": "5s",
117 | "schemaVersion": 38,
118 | "style": "dark",
119 | "tags": [],
120 | "templating": {
121 | "list": []
122 | },
123 | "time": {
124 | "from": "now-5m",
125 | "to": "now"
126 | },
127 | "timepicker": {},
128 | "timezone": "",
129 | "title": "Message Rate out",
130 | "uid": "b9031b09-082f-4dc6-9b82-766c5e543328",
131 | "version": 2,
132 | "weekStart": ""
133 | }
134 |
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/messagesin.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 11,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "mode": "palette-classic"
34 | },
35 | "custom": {
36 | "axisCenteredZero": false,
37 | "axisColorMode": "text",
38 | "axisLabel": "",
39 | "axisPlacement": "auto",
40 | "barAlignment": 0,
41 | "drawStyle": "line",
42 | "fillOpacity": 14,
43 | "gradientMode": "opacity",
44 | "hideFrom": {
45 | "legend": false,
46 | "tooltip": false,
47 | "viz": false
48 | },
49 | "lineInterpolation": "smooth",
50 | "lineWidth": 1,
51 | "pointSize": 5,
52 | "scaleDistribution": {
53 | "type": "linear"
54 | },
55 | "showPoints": "auto",
56 | "spanNulls": false,
57 | "stacking": {
58 | "group": "A",
59 | "mode": "none"
60 | },
61 | "thresholdsStyle": {
62 | "mode": "off"
63 | }
64 | },
65 | "mappings": [],
66 | "thresholds": {
67 | "mode": "absolute",
68 | "steps": [
69 | {
70 | "color": "green",
71 | "value": null
72 | },
73 | {
74 | "color": "red",
75 | "value": 80
76 | }
77 | ]
78 | }
79 | },
80 | "overrides": [
81 | {
82 | "matcher": {
83 | "id": "byName",
84 | "options": "pulsar_in_messages_total{cluster=\"cluster-a\", instance=\"broker:8080\", job=\"pulsar\", namespace=\"public/default\", topic=\"persistent://public/default/my-topic\"}"
85 | },
86 | "properties": [
87 | {
88 | "id": "color",
89 | "value": {
90 | "fixedColor": "dark-purple",
91 | "mode": "fixed"
92 | }
93 | }
94 | ]
95 | },
96 | {
97 | "matcher": {
98 | "id": "byName",
99 | "options": "pulsar_in_messages_total{cluster=\"cluster-a\", instance=\"broker:8080\", job=\"pulsar\", namespace=\"public/default\", topic=\"persistent://public/default/another-topic\"}"
100 | },
101 | "properties": [
102 | {
103 | "id": "color",
104 | "value": {
105 | "fixedColor": "dark-red",
106 | "mode": "fixed"
107 | }
108 | }
109 | ]
110 | }
111 | ]
112 | },
113 | "gridPos": {
114 | "h": 8,
115 | "w": 12,
116 | "x": 0,
117 | "y": 0
118 | },
119 | "id": 1,
120 | "options": {
121 | "legend": {
122 | "calcs": [],
123 | "displayMode": "list",
124 | "placement": "bottom",
125 | "showLegend": false
126 | },
127 | "tooltip": {
128 | "mode": "single",
129 | "sort": "none"
130 | }
131 | },
132 | "targets": [
133 | {
134 | "datasource": {
135 | "type": "prometheus",
136 | "uid": "PBFA97CFB590B2093"
137 | },
138 | "editorMode": "builder",
139 | "expr": "pulsar_in_messages_total{topic!=\"persistent://public/default/__change_events\"}",
140 | "instant": false,
141 | "range": true,
142 | "refId": "A"
143 | }
144 | ],
145 | "title": " ",
146 | "transparent": true,
147 | "type": "timeseries"
148 | }
149 | ],
150 | "refresh": "5s",
151 | "schemaVersion": 38,
152 | "style": "dark",
153 | "tags": [],
154 | "templating": {
155 | "list": []
156 | },
157 | "time": {
158 | "from": "now-5m",
159 | "to": "now"
160 | },
161 | "timepicker": {},
162 | "timezone": "",
163 | "title": "newmessagesin",
164 | "uid": "c2ef0e4a-da28-4797-bd86-5c1957aa9039",
165 | "version": 10,
166 | "weekStart": ""
167 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/messagesout.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 10,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "mode": "palette-classic"
34 | },
35 | "custom": {
36 | "axisCenteredZero": false,
37 | "axisColorMode": "text",
38 | "axisLabel": "",
39 | "axisPlacement": "auto",
40 | "barAlignment": 0,
41 | "drawStyle": "line",
42 | "fillOpacity": 14,
43 | "gradientMode": "opacity",
44 | "hideFrom": {
45 | "legend": false,
46 | "tooltip": false,
47 | "viz": false
48 | },
49 | "lineInterpolation": "smooth",
50 | "lineWidth": 1,
51 | "pointSize": 5,
52 | "scaleDistribution": {
53 | "type": "linear"
54 | },
55 | "showPoints": "auto",
56 | "spanNulls": false,
57 | "stacking": {
58 | "group": "A",
59 | "mode": "none"
60 | },
61 | "thresholdsStyle": {
62 | "mode": "off"
63 | }
64 | },
65 | "mappings": [],
66 | "thresholds": {
67 | "mode": "absolute",
68 | "steps": [
69 | {
70 | "color": "green",
71 | "value": null
72 | },
73 | {
74 | "color": "red",
75 | "value": 80
76 | }
77 | ]
78 | }
79 | },
80 | "overrides": [
81 | {
82 | "matcher": {
83 | "id": "byName",
84 | "options": "pulsar_out_messages_total{cluster=\"cluster-a\", instance=\"broker:8080\", job=\"pulsar\", namespace=\"public/default\", subscription=\"my-subscription\", topic=\"persistent://public/default/another-topic\"}"
85 | },
86 | "properties": [
87 | {
88 | "id": "color",
89 | "value": {
90 | "fixedColor": "dark-red",
91 | "mode": "fixed"
92 | }
93 | }
94 | ]
95 | },
96 | {
97 | "matcher": {
98 | "id": "byName",
99 | "options": "pulsar_out_messages_total{cluster=\"cluster-a\", instance=\"broker:8080\", job=\"pulsar\", namespace=\"public/default\", subscription=\"my-subscription\", topic=\"persistent://public/default/my-topic\"}"
100 | },
101 | "properties": [
102 | {
103 | "id": "color",
104 | "value": {
105 | "fixedColor": "dark-purple",
106 | "mode": "fixed"
107 | }
108 | }
109 | ]
110 | }
111 | ]
112 | },
113 | "gridPos": {
114 | "h": 8,
115 | "w": 12,
116 | "x": 0,
117 | "y": 0
118 | },
119 | "id": 1,
120 | "options": {
121 | "legend": {
122 | "calcs": [],
123 | "displayMode": "list",
124 | "placement": "bottom",
125 | "showLegend": false
126 | },
127 | "tooltip": {
128 | "mode": "single",
129 | "sort": "none"
130 | }
131 | },
132 | "targets": [
133 | {
134 | "datasource": {
135 | "type": "prometheus",
136 | "uid": "PBFA97CFB590B2093"
137 | },
138 | "editorMode": "builder",
139 | "expr": "pulsar_out_messages_total{topic!=\"persistent://public/default/__change_events\"}",
140 | "instant": false,
141 | "range": true,
142 | "refId": "A"
143 | }
144 | ],
145 | "title": " ",
146 | "transparent": true,
147 | "type": "timeseries"
148 | }
149 | ],
150 | "refresh": "5s",
151 | "schemaVersion": 38,
152 | "style": "dark",
153 | "tags": [],
154 | "templating": {
155 | "list": []
156 | },
157 | "time": {
158 | "from": "now-5m",
159 | "to": "now"
160 | },
161 | "timepicker": {},
162 | "timezone": "",
163 | "title": "messagesout",
164 | "uid": "b7cc76a7-51f0-4175-bbbc-dada7ec3fa2d",
165 | "version": 12,
166 | "weekStart": ""
167 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/throughputin.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 8,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "fixedColor": "semi-dark-blue",
34 | "mode": "fixed"
35 | },
36 | "custom": {
37 | "axisCenteredZero": false,
38 | "axisColorMode": "text",
39 | "axisLabel": "",
40 | "axisPlacement": "auto",
41 | "barAlignment": 0,
42 | "drawStyle": "line",
43 | "fillOpacity": 14,
44 | "gradientMode": "opacity",
45 | "hideFrom": {
46 | "legend": false,
47 | "tooltip": false,
48 | "viz": false
49 | },
50 | "lineInterpolation": "smooth",
51 | "lineWidth": 1,
52 | "pointSize": 5,
53 | "scaleDistribution": {
54 | "type": "linear"
55 | },
56 | "showPoints": "auto",
57 | "spanNulls": false,
58 | "stacking": {
59 | "group": "A",
60 | "mode": "none"
61 | },
62 | "thresholdsStyle": {
63 | "mode": "off"
64 | }
65 | },
66 | "mappings": [],
67 | "thresholds": {
68 | "mode": "absolute",
69 | "steps": [
70 | {
71 | "color": "green",
72 | "value": null
73 | }
74 | ]
75 | },
76 | "unit": "Bps"
77 | },
78 | "overrides": []
79 | },
80 | "gridPos": {
81 | "h": 8,
82 | "w": 12,
83 | "x": 0,
84 | "y": 0
85 | },
86 | "id": 1,
87 | "options": {
88 | "legend": {
89 | "calcs": [],
90 | "displayMode": "list",
91 | "placement": "bottom",
92 | "showLegend": false
93 | },
94 | "tooltip": {
95 | "mode": "single",
96 | "sort": "none"
97 | }
98 | },
99 | "pluginVersion": "10.0.1",
100 | "targets": [
101 | {
102 | "datasource": {
103 | "type": "prometheus",
104 | "uid": "PBFA97CFB590B2093"
105 | },
106 | "editorMode": "builder",
107 | "expr": "pulsar_broker_throughput_in",
108 | "instant": false,
109 | "range": true,
110 | "refId": "A"
111 | }
112 | ],
113 | "title": " ",
114 | "transparent": true,
115 | "type": "timeseries"
116 | }
117 | ],
118 | "refresh": "5s",
119 | "schemaVersion": 38,
120 | "style": "dark",
121 | "tags": [],
122 | "templating": {
123 | "list": []
124 | },
125 | "time": {
126 | "from": "now-5m",
127 | "to": "now"
128 | },
129 | "timepicker": {},
130 | "timezone": "",
131 | "title": "throughputin",
132 | "uid": "bdfb696f-55e4-46da-aa3d-381b5cf2f408",
133 | "version": 5,
134 | "weekStart": ""
135 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/throughputout.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 9,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "fixedColor": "semi-dark-blue",
34 | "mode": "fixed"
35 | },
36 | "custom": {
37 | "axisCenteredZero": false,
38 | "axisColorMode": "text",
39 | "axisLabel": "",
40 | "axisPlacement": "auto",
41 | "barAlignment": 0,
42 | "drawStyle": "line",
43 | "fillOpacity": 14,
44 | "gradientMode": "opacity",
45 | "hideFrom": {
46 | "legend": false,
47 | "tooltip": false,
48 | "viz": false
49 | },
50 | "lineInterpolation": "smooth",
51 | "lineWidth": 1,
52 | "pointSize": 5,
53 | "scaleDistribution": {
54 | "type": "linear"
55 | },
56 | "showPoints": "auto",
57 | "spanNulls": false,
58 | "stacking": {
59 | "group": "A",
60 | "mode": "none"
61 | },
62 | "thresholdsStyle": {
63 | "mode": "off"
64 | }
65 | },
66 | "mappings": [],
67 | "thresholds": {
68 | "mode": "percentage",
69 | "steps": [
70 | {
71 | "color": "green",
72 | "value": null
73 | }
74 | ]
75 | },
76 | "unit": "Bps"
77 | },
78 | "overrides": []
79 | },
80 | "gridPos": {
81 | "h": 8,
82 | "w": 12,
83 | "x": 0,
84 | "y": 0
85 | },
86 | "id": 1,
87 | "options": {
88 | "legend": {
89 | "calcs": [],
90 | "displayMode": "list",
91 | "placement": "bottom",
92 | "showLegend": false
93 | },
94 | "tooltip": {
95 | "mode": "single",
96 | "sort": "none"
97 | }
98 | },
99 | "targets": [
100 | {
101 | "datasource": {
102 | "type": "prometheus",
103 | "uid": "PBFA97CFB590B2093"
104 | },
105 | "editorMode": "builder",
106 | "expr": "pulsar_broker_throughput_out",
107 | "instant": false,
108 | "range": true,
109 | "refId": "A"
110 | }
111 | ],
112 | "title": " ",
113 | "transparent": true,
114 | "type": "timeseries"
115 | }
116 | ],
117 | "refresh": "5s",
118 | "schemaVersion": 38,
119 | "style": "dark",
120 | "tags": [],
121 | "templating": {
122 | "list": []
123 | },
124 | "time": {
125 | "from": "now-5m",
126 | "to": "now"
127 | },
128 | "timepicker": {},
129 | "timezone": "",
130 | "title": "throughtputout",
131 | "uid": "b1f5eb24-9c75-4ba3-a40b-7bf8e1ccdc94",
132 | "version": 5,
133 | "weekStart": ""
134 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/topicsandsubscriptions.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 14,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "fixedColor": "light-purple",
34 | "mode": "fixed"
35 | },
36 | "mappings": [],
37 | "thresholds": {
38 | "mode": "absolute",
39 | "steps": [
40 | {
41 | "color": "green",
42 | "value": null
43 | }
44 | ]
45 | },
46 | "unit": "short"
47 | },
48 | "overrides": [
49 | {
50 | "matcher": {
51 | "id": "byName",
52 | "options": "pulsar_broker_topics_count"
53 | },
54 | "properties": [
55 | {
56 | "id": "displayName",
57 | "value": "Topics"
58 | }
59 | ]
60 | },
61 | {
62 | "matcher": {
63 | "id": "byName",
64 | "options": "pulsar_broker_subscriptions_count"
65 | },
66 | "properties": [
67 | {
68 | "id": "displayName",
69 | "value": "Subscriptions"
70 | }
71 | ]
72 | }
73 | ]
74 | },
75 | "gridPos": {
76 | "h": 8,
77 | "w": 12,
78 | "x": 0,
79 | "y": 0
80 | },
81 | "id": 1,
82 | "options": {
83 | "colorMode": "background",
84 | "graphMode": "none",
85 | "justifyMode": "center",
86 | "orientation": "auto",
87 | "reduceOptions": {
88 | "calcs": [
89 | "lastNotNull"
90 | ],
91 | "fields": "",
92 | "values": false
93 | },
94 | "textMode": "auto"
95 | },
96 | "pluginVersion": "10.0.1",
97 | "targets": [
98 | {
99 | "datasource": {
100 | "type": "prometheus",
101 | "uid": "PBFA97CFB590B2093"
102 | },
103 | "editorMode": "builder",
104 | "expr": "pulsar_broker_topics_count",
105 | "instant": false,
106 | "range": true,
107 | "refId": "Producers"
108 | },
109 | {
110 | "datasource": {
111 | "type": "prometheus",
112 | "uid": "PBFA97CFB590B2093"
113 | },
114 | "editorMode": "builder",
115 | "expr": "pulsar_broker_subscriptions_count",
116 | "hide": false,
117 | "instant": false,
118 | "range": true,
119 | "refId": "A"
120 | }
121 | ],
122 | "title": " ",
123 | "transparent": true,
124 | "type": "stat"
125 | }
126 | ],
127 | "refresh": "5s",
128 | "schemaVersion": 38,
129 | "style": "dark",
130 | "tags": [],
131 | "templating": {
132 | "list": []
133 | },
134 | "time": {
135 | "from": "now-5m",
136 | "to": "now"
137 | },
138 | "timepicker": {},
139 | "timezone": "",
140 | "title": "topicsandsubscriptions",
141 | "uid": "c8268214-d094-46d5-9a25-7cce1192ae69",
142 | "version": 8,
143 | "weekStart": ""
144 | }
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/dashboards/usageGauge.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": {
7 | "type": "grafana",
8 | "uid": "-- Grafana --"
9 | },
10 | "enable": true,
11 | "hide": true,
12 | "iconColor": "rgba(0, 211, 255, 1)",
13 | "name": "Annotations & Alerts",
14 | "type": "dashboard"
15 | }
16 | ]
17 | },
18 | "editable": true,
19 | "fiscalYearStartMonth": 0,
20 | "graphTooltip": 0,
21 | "id": 5,
22 | "links": [],
23 | "liveNow": false,
24 | "panels": [
25 | {
26 | "datasource": {
27 | "type": "prometheus",
28 | "uid": "PBFA97CFB590B2093"
29 | },
30 | "fieldConfig": {
31 | "defaults": {
32 | "color": {
33 | "mode": "thresholds"
34 | },
35 | "mappings": [],
36 | "thresholds": {
37 | "mode": "absolute",
38 | "steps": [
39 | {
40 | "color": "dark-purple",
41 | "value": null
42 | },
43 | {
44 | "color": "semi-dark-orange",
45 | "value": 70
46 | },
47 | {
48 | "color": "semi-dark-red",
49 | "value": 85
50 | }
51 | ]
52 | },
53 | "unit": "percent"
54 | },
55 | "overrides": [
56 | {
57 | "matcher": {
58 | "id": "byName",
59 | "options": "pulsar_lb_cpu_usage"
60 | },
61 | "properties": [
62 | {
63 | "id": "displayName",
64 | "value": "CPU Usage"
65 | }
66 | ]
67 | },
68 | {
69 | "matcher": {
70 | "id": "byName",
71 | "options": "pulsar_lb_memory_usage"
72 | },
73 | "properties": [
74 | {
75 | "id": "displayName",
76 | "value": "Memory Usage"
77 | }
78 | ]
79 | }
80 | ]
81 | },
82 | "gridPos": {
83 | "h": 8,
84 | "w": 12,
85 | "x": 0,
86 | "y": 0
87 | },
88 | "id": 1,
89 | "options": {
90 | "orientation": "auto",
91 | "reduceOptions": {
92 | "calcs": ["lastNotNull"],
93 | "fields": "",
94 | "values": false
95 | },
96 | "showThresholdLabels": false,
97 | "showThresholdMarkers": true
98 | },
99 | "pluginVersion": "10.0.2",
100 | "targets": [
101 | {
102 | "datasource": {
103 | "type": "prometheus",
104 | "uid": "PBFA97CFB590B2093"
105 | },
106 | "editorMode": "builder",
107 | "expr": "pulsar_lb_cpu_usage",
108 | "instant": false,
109 | "range": true,
110 | "refId": "A"
111 | },
112 | {
113 | "datasource": {
114 | "type": "prometheus",
115 | "uid": "PBFA97CFB590B2093"
116 | },
117 | "editorMode": "builder",
118 | "expr": "pulsar_lb_memory_usage",
119 | "hide": false,
120 | "instant": false,
121 | "range": true,
122 | "refId": "B"
123 | }
124 | ],
125 | "title": " ",
126 | "transparent": true,
127 | "type": "gauge"
128 | }
129 | ],
130 | "refresh": "5s",
131 | "schemaVersion": 38,
132 | "style": "dark",
133 | "tags": [],
134 | "templating": {
135 | "list": []
136 | },
137 | "time": {
138 | "from": "now-6h",
139 | "to": "now"
140 | },
141 | "timepicker": {},
142 | "timezone": "",
143 | "title": "New dashboard",
144 | "uid": "a6f621d3-bb3c-4569-8315-2e0c4b2de35b",
145 | "version": 2,
146 | "weekStart": ""
147 | }
148 |
--------------------------------------------------------------------------------
/imageConfigs/grafana/provisioning/datasources/srcProm.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | datasources:
4 | - name: Prometheus
5 | type: prometheus
6 | access: proxy
7 | orgId: 1
8 | # url: http://localhost:9090
9 | url: http://prometheus:9090
10 | basicAuth: false
11 | isDefault: true
12 | editable: true
13 |
--------------------------------------------------------------------------------
/imageConfigs/prometheus/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM prom/prometheus
2 |
3 | COPY config/prometheus.yml /etc/prometheus/prometheus.yml
4 |
5 | ENV PULSAR_BROKER_PORT=8080
6 |
7 | CMD [ "--config.file=/etc/prometheus/prometheus.yml" ]
--------------------------------------------------------------------------------
/imageConfigs/prometheus/config/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 5s
3 | evaluation_interval: 15s
4 |
5 | scrape_configs:
6 | - job_name: 'pulsar'
7 | scrape_interval: 5s
8 | metrics_path: '/metrics/'
9 | static_configs:
10 | - targets: ['host.docker.internal:8080']
11 | # - targets: ['broker:8080']
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Pulsar Portrait
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './client/styles/styles.scss'
5 |
6 | const root = ReactDOM.createRoot(document.getElementById('root'))
7 | root.render(
8 | <>
9 |
10 | >
11 | )
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pulsarportrait",
3 | "version": "1.0.0",
4 | "description": "A pulsar visualizer",
5 | "main": "index.tsx",
6 | "scripts": {
7 | "test": "jest --verbose",
8 | "dockbuild": "docker build -t pulsarportrait .",
9 | "dockprom": "docker build -t pulsarproto .",
10 | "server": "nodemon ./server/server.js",
11 | "dev": "NODE_ENV=development nodemon server/server.js & NODE_ENV=development webpack serve --open",
12 | "start": "webpack-dev-server --mode development --open --hot",
13 | "build": "webpack --mode production"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/oslabs-beta/PulsarPortrait.git"
18 | },
19 | "author": "Grant Thomas, Jordan Zolman, Cyrux Lam, Anthony Le",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/oslabs-beta/PulsarPortrait/issues"
23 | },
24 | "homepage": "https://github.com/oslabs-beta/PulsarPortrait#readme",
25 | "jest": {
26 | "transform": {
27 | "^.+\\.[jt]sx?$": "babel-jest"
28 | },
29 | "testEnvironment": "jest-environment-jsdom"
30 | },
31 | "dependencies": {
32 | "@emotion/react": "^11.11.1",
33 | "@emotion/styled": "^11.11.0",
34 | "@mui/icons-material": "^5.13.7",
35 | "@mui/material": "^5.13.7",
36 | "cors-anywhere": "^0.4.4",
37 | "css-loader": "^6.8.1",
38 | "express": "^4.18.2",
39 | "express-session": "^1.17.3",
40 | "frameguard": "^4.0.0",
41 | "react": "^18.2.0",
42 | "react-dom": "^18.2.0",
43 | "react-icons": "^4.10.1",
44 | "sass": "^1.63.6",
45 | "sass-loader": "^13.3.2",
46 | "ts-loader": "^9.4.4",
47 | "ts-node": "^10.9.1",
48 | "typescript": "^5.1.6"
49 | },
50 | "devDependencies": {
51 | "@babel/core": "^7.22.5",
52 | "@babel/plugin-transform-modules-commonjs": "^7.22.5",
53 | "@babel/preset-env": "^7.22.5",
54 | "@babel/preset-react": "^7.22.3",
55 | "@babel/preset-typescript": "^7.22.5",
56 | "@jest/globals": "^27.5.1",
57 | "@testing-library/jest-dom": "^5.16.5",
58 | "@testing-library/react": "^13.4.0",
59 | "@types/express": "^4.17.13",
60 | "@types/jest": "^29.2.3",
61 | "@types/node": "^17.0.8",
62 | "@types/react": "^18.2.8",
63 | "@types/react-dom": "^18.2.4",
64 | "babel-jest": "^29.5.0",
65 | "babel-loader": "^9.1.2",
66 | "babel-polyfill": "^6.26.0",
67 | "eslint": "^8.42.0",
68 | "html-webpack-plugin": "^5.5.1",
69 | "jest": "^28.1.3",
70 | "jest-environment-jsdom": "^28.1.0",
71 | "mongoose": "^7.2.2",
72 | "nodemon": "^1.14.9",
73 | "react-test-renderer": "^18.2.0",
74 | "style-loader": "^3.3.3",
75 | "webpack": "^5.85.1",
76 | "webpack-cli": "^5.1.3",
77 | "webpack-dev-server": "^4.15.0"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/server/server.ts:
--------------------------------------------------------------------------------
1 | // const express = require('express');
2 | import express, { Request, Response, NextFunction, RequestHandler } from 'express'
3 | // const {Request, Response, NextFunction, RequestHandles} = require('express')
4 | // const path = require('path');
5 | import path from 'path';
6 | const app = express();
7 | const PORT = process.env.PORT || 3333;
8 |
9 | type ServerError = {
10 | log: string,
11 | status: number,
12 | message: {}
13 | }
14 |
15 | app.use(express.json());
16 | app.use(express.urlencoded({ extended: true }));
17 |
18 | // statically serve the dist and client folder
19 | app.use(express.static(path.join(__dirname, '../dist')));
20 | app.use(express.static(path.join(__dirname, '../client')));
21 |
22 | app.get('/', (req: Request, res: Response) => {
23 | res.sendFile(path.join(__dirname, '../client/index.html'));
24 | });
25 |
26 | //route error handler
27 | app.use((req: Request, res: Response) =>
28 | res.status(404).send('Error, not the page you are looking for')
29 | );
30 |
31 | // global error handler to handle erros within middleware
32 | app.use((err: ServerError, req: Request, res: Response, next: NextFunction) => {
33 | //Define a default error object
34 | const defaultErr: ServerError = {
35 | log: 'Express error handler caught unknown middleware error',
36 | status: 400,
37 | message: { err: 'An error occured' },
38 | };
39 | // define an errorObj to combine new errors
40 | const errObj: ServerError = Object.assign(defaultErr, err);
41 | console.log('Error: ', errObj.log);
42 | // return to the client the status and error message
43 | return res.status(errObj.status || 500).send(errObj.message);
44 | });
45 |
46 | app.listen(PORT, () => console.log(`Listening on port ${PORT}...`));
47 |
48 | module.exports = app;
49 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "commonjs",
5 | "removeComments": true,
6 | "jsx": "react",
7 | "allowJs": true,
8 | "noImplicitAny": true,
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "strictNullChecks": false
12 | },
13 | "include": ["client/**/*", "index.tsx", "server/server.ts"]
14 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HTMLWebpackPlugin = require('html-webpack-plugin');
3 |
4 |
5 | module.exports = {
6 | entry: [
7 | path.resolve(__dirname, './index.tsx'),
8 |
9 | ],
10 | output: {
11 | path: path.join(__dirname, '/dist'),
12 | filename: 'bundle.js',
13 |
14 | },
15 | devServer: {
16 | host: 'localhost',
17 | port: 7080,
18 | static: {
19 | directory: path.resolve(__dirname, 'dist'),
20 | // match the output 'publicPath'
21 | publicPath: '/',
22 | },
23 | // enable HMR on the devServer
24 | hot: true,
25 | // fallback to root for other urls
26 | historyApiFallback: true,
27 | headers: { 'Access-Control-Allow-Origin': '*' },
28 | compress: true,
29 | proxy: {
30 | '/': 'http://localhost:3333'
31 | }
32 | },
33 |
34 | plugins: [
35 | new HTMLWebpackPlugin({
36 | template: './client/index.html'
37 | })
38 | ],
39 |
40 | module: {
41 | rules: [
42 | {
43 | test: /.jsx?$/,
44 | exclude: /node_modules/,
45 | use: {
46 | loader: 'babel-loader',
47 | options: {
48 | presets: ['@babel/preset-env', '@babel/preset-react']
49 | }
50 | }
51 | },
52 | {
53 | test: /.tsx?$/,
54 | exclude: /node_modules/,
55 | use: {
56 | loader: 'ts-loader'
57 | }
58 | },
59 | {
60 | test: /\.s[ac]ss$/,
61 | use: ["style-loader", "css-loader", 'sass-loader']
62 | }
63 | ]
64 | },
65 | resolve: {
66 | // Enable importing JS / JSX / TS / TSX files without specifying their extension
67 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
68 | },
69 | }
--------------------------------------------------------------------------------