Skip to content
This repository has been archived by the owner on Mar 27, 2023. It is now read-only.

Commit

Permalink
Switch from API host to path prefix
Browse files Browse the repository at this point in the history
- Remove configuration
- Add integrated mock server
  • Loading branch information
Michel Zimmer committed Aug 31, 2022
1 parent 2cd688d commit d7aa555
Show file tree
Hide file tree
Showing 20 changed files with 201 additions and 91 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
**/*
!index.html
!mocks/
!nginx.conf
!package.json
!public/
!src/
!tsconfig.json
!tsconfig.node.json
!vite-plugin-serve-handler/
!vite.config.ts
!yarn.lock
1 change: 0 additions & 1 deletion .env.development

This file was deleted.

4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ COPY --chown=node:node \
tsconfig.node.json \
vite.config.ts \
./
COPY --chown=node:node mocks ./mocks
COPY --chown=node:node public ./public
COPY --chown=node:node src ./src
COPY --chown=node:node vite-plugin-serve-handler ./vite-plugin-serve-handler
RUN yarn build
FROM nginxinc/nginx-unprivileged:alpine
COPY nginx.conf /etc/nginx/templates/default.conf.template
Expand All @@ -24,5 +26,5 @@ LABEL org.opencontainers.image.vendor="neuland – Büro für Informatik GmbH"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.title="bandwhichd-ui"
LABEL org.opencontainers.image.description="bandwhichd ui displaying network topology and statistics"
LABEL org.opencontainers.image.version="0.3.0"
LABEL org.opencontainers.image.version="0.4.0"
COPY --from=build --chown=root:root /home/node/dist /usr/share/nginx/html
7 changes: 3 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ services:
main:
build:
context: .
env_file:
- .env.development
ports:
- 3000:8080
environment:
BANDWHICHD_API_SERVER: http://127.0.0.1:8080
network_mode: host
1 change: 0 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<meta charset="UTF-8" />
<meta name="description" content="bandwhichd ui displaying network topology and statistics" />
<title>bandwhichd-ui</title>
<script>document.configuration={apiServer:"PLACEHOLDER_API_SERVER"}</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
47 changes: 0 additions & 47 deletions mock-server.mjs

This file was deleted.

105 changes: 105 additions & 0 deletions mocks/apiHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import fs from "fs"
import http from "http";
import path from "path"
import process from "process"
import type { Connect, ResolvedConfig } from "vite";

import { ServeHandler } from "../vite-plugin-serve-handler";

const isApiRoute: (url: string) => boolean =
(url) => url === "/api" || url.startsWith("/api?") || url.startsWith("/api/");

const bandwhichdApiServerFromEnv: (viteConfig: ResolvedConfig) => URL | null =
(viteConfig) => {
const bandwhichdApiServer = viteConfig.env["BANDWHICHD_API_SERVER"];
try {
return new URL(bandwhichdApiServer);
} catch (_) {
return null;
}
};

const handleWithMocks =
(request: Connect.IncomingMessage, response: http.ServerResponse) => {
const chunks = [];
request.on("data", chunk => {
chunks.push(chunk);
});

request.on("end", () => {
if (request.method !== "GET"
|| request.url !== "/api/v1/stats") {
response.writeHead(404);
response.end();
return;
}

if (chunks.length > 0) {
response.writeHead(400);
response.end();
return;
}

const format =
request.headers.accept === "text/vnd.graphviz; q=1.0"
? "dot"
: "json";

const filePath = path.join(process.cwd(), 'mocks', `stats.${format}`);
const fileStat = fs.statSync(filePath);

response.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Content-Length": fileStat.size,
});
fs.createReadStream(filePath).pipe(response);
});
};

const handleWithServer =
(request: Connect.IncomingMessage, response: http.ServerResponse, viteConfig: ResolvedConfig, bandwhichdApiServer: URL) => {
const upstreamRequestOptions: http.RequestOptions = {
protocol: bandwhichdApiServer.protocol,
host: bandwhichdApiServer.hostname,
port: bandwhichdApiServer.port,
method: request.method,
path: request.url.substring("/api".length),
headers: {
...request.headers,
host: `${bandwhichdApiServer.host}`,
},
};

request.pipe(http.request(upstreamRequestOptions, (upstreamResponse) => {
upstreamResponse.pipe(response).on("error", (error) => {
viteConfig.logger.error("mocks/apiHandler: Error proxying response", {
error,
timestamp: true
});
response.end();
});
})).on("error", (error) => {
viteConfig.logger.error("mocks/apiHandler: Error proxying request", {
error,
timestamp: true
});
response.end();
});
};

export const apiHandler: ServeHandler.Handler =
(request, response, viteConfig) => {
if (!isApiRoute(request.url)) {
return false;
}

const bandwhichdApiServer = bandwhichdApiServerFromEnv(viteConfig);

if (bandwhichdApiServer === null) {
handleWithMocks(request, response);
} else {
handleWithServer(request, response, viteConfig, bandwhichdApiServer);
}

return true;
};
File renamed without changes.
1 change: 1 addition & 0 deletions mocks/stats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"hosts":{"28e9f9f4-c9ec-7448-d591-f8fce34086ce":{"hostname":"spring-db1","additional_hostnames":[],"connections":{}},"d416fa7d-edef-ded2-5074-3244110a5a3d":{"hostname":"spring-mongodb","additional_hostnames":[],"connections":{}},"fda7ecc9-eb5f-002d-2d11-eb4ab88ef9e4":{"hostname":"spring-logging","additional_hostnames":[],"connections":{}},"b110d228-d4c9-667b-c4da-913425b25175":{"hostname":"ecom-nginx-services","additional_hostnames":[],"connections":{}},"3509c6bc-b535-a72a-2bc1-f5b585e03063":{"hostname":"ecom-cache2","additional_hostnames":[],"connections":{}},"dda70a41-0ee2-8e2c-dc15-1a12cd46b7e4":{"hostname":"ecom-app-live1","additional_hostnames":[],"connections":{}},"f6541cc6-8eea-8e2c-d40b-959e18660923":{"hostname":"ecom-cache","additional_hostnames":[],"connections":{}},"55faca59-16af-34b8-9f0d-882b6531e82d":{"hostname":"spring-staging","additional_hostnames":[],"connections":{}},"2b09a772-335d-d36b-1cfb-eb75f6c9bea1":{"hostname":"spring-app-live2","additional_hostnames":[],"connections":{}},"fd4760d6-baa4-3b27-3bf0-a83174eb5014":{"hostname":"spring-web-live6","additional_hostnames":[],"connections":{}},"35b2b94f-41f9-c120-8852-af0665d5b628":{"hostname":"spring-cache","additional_hostnames":[],"connections":{}},"6075cc26-5b1b-5f01-f0d8-2a78e139a0d2":{"hostname":"spring-cache2","additional_hostnames":[],"connections":{}},"1c9684d1-cb83-501e-63da-9077e9868a98":{"hostname":"spring-services1","additional_hostnames":[],"connections":{}},"2000044d-80c2-fb2f-80a7-30345cea90ff":{"hostname":"ecom-services1","additional_hostnames":[],"connections":{}},"eebbdbb0-1fdc-7802-8b35-6a5f84699eb5":{"hostname":"ecom-services2","additional_hostnames":[],"connections":{}},"045c74d2-e4f6-6c5d-76fa-a55a3b23be6e":{"hostname":"ecom-logging","additional_hostnames":[],"connections":{}},"0ead0515-5d04-9c5a-9a8d-9724d34882ee":{"hostname":"ecom-app-live2","additional_hostnames":[],"connections":{}},"78334682-0244-2adb-6cf2-c243717a3f58":{"hostname":"ecom-web-live3","additional_hostnames":[],"connections":{}},"71d66f34-2bc1-7853-bc9d-0ee87a963264":{"hostname":"ecom-web-live6","additional_hostnames":[],"connections":{}},"a5a0f816-8cb2-a634-9dc5-6d28dbbef6e3":{"hostname":"spring-staging2","additional_hostnames":[],"connections":{}},"40b13e86-8d85-816d-bbe4-09d223eaf94c":{"hostname":"spring-nginx-services","additional_hostnames":[],"connections":{}},"0add42fd-d09b-13ad-62d0-9d7edf87e7b1":{"hostname":"spring-app-live1","additional_hostnames":[],"connections":{}},"98bc86ae-6dd9-05a0-4119-851e82b84dc2":{"hostname":"spring-services2","additional_hostnames":[],"connections":{}},"2c22b189-ed70-9ad1-b758-54bd5b1aef4b":{"hostname":"spring-web-live3","additional_hostnames":[],"connections":{}}},"unmonitoredHosts":{}}
12 changes: 9 additions & 3 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
server {
listen 8080;
listen [::]:8080;
listen 3000;
listen [::]:3000;

location ~ ^/api(/.*)?$ {
proxy_pass ${BANDWHICHD_API_SERVER}$1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}

location / {
root /usr/share/nginx/html;
index index.html;
sub_filter 'PLACEHOLDER_API_SERVER' '${BANDWHICHD_API_SERVER}';
}
}
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bandwhichd-ui",
"version": "0.3.0",
"version": "0.4.0",
"description": "bandwhichd ui displaying network topology and statistics",
"license": "MIT",
"private": true,
Expand All @@ -9,8 +9,7 @@
"dev": "vite --port=3000",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "jest",
"mock-server": "node mock-server.mjs"
"test": "jest"
},
"dependencies": {
"fp-ts": "^2.12.2",
Expand Down
13 changes: 0 additions & 13 deletions src/Configuration.ts

This file was deleted.

7 changes: 3 additions & 4 deletions src/Graph.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useEffect, useRef, useState } from "react";
import * as VisNetwork from "vis-network";
import { Configuration, configuration } from "./Configuration";
import { HostId } from "./Stats";

import styles from "./Graph.module.css";

const fetchData = async (configuration: Configuration): Promise<string> => {
const response = await window.fetch(`${configuration.apiServer}/v1/stats`, {
const fetchData = async (): Promise<string> => {
const response = await window.fetch("/api/v1/stats", {
method: "GET",
headers: {
"Accept": "text/vnd.graphviz; q=1.0"
Expand All @@ -33,7 +32,7 @@ export const Graph: React.FC<GraphProps> =
const container = containerRef.current;

setIsLoading(true);
fetchData(configuration).then(data => {
fetchData().then(data => {
// @ts-ignore
const parsedData = VisNetwork.parseDOTNetwork(data);
parsedData.options.physics = {
Expand Down
3 changes: 1 addition & 2 deletions src/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from "react";
import { configuration } from "./Configuration";

import styles from "./Header.module.css"

export const Header: React.FC =
() => <header className={styles.header}>Connected to <code>{configuration.apiServer}</code></header>;
() => <header className={styles.header}>Timeframe: 2 hours</header>;
3 changes: 1 addition & 2 deletions src/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import { configuration } from "./Configuration";
import { Graph } from "./Graph";
import { HostDetails } from "./HostDetails";

Expand All @@ -23,7 +22,7 @@ export const Main: React.FC =
: { hostId: maybeSelectedHostId, ...maybeSelectedHostWithoutId };

useEffect(() => {
fetchStats(configuration).then(stats => {
fetchStats().then(stats => {
setMaybeStats(stats);
}).catch(console.error);
}, []);
Expand Down
5 changes: 2 additions & 3 deletions src/Stats.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Configuration } from "./Configuration";
import { Map } from "immutable";
import * as Decoder from "io-ts/lib/Decoder"
import { mapDecoder } from "./lib/immutable/io-ts/mapDecoder";
Expand Down Expand Up @@ -45,8 +44,8 @@ const statsDecoder = Decoder.struct({
});

export const fetchStats =
async (configuration: Configuration): Promise<Stats> =>
await window.fetch(`${configuration.apiServer}/v1/stats`, {
async (): Promise<Stats> =>
await window.fetch("/api/v1/stats", {
method: "GET",
headers: {
"Accept": "application/json; q=1.0"
Expand Down
1 change: 0 additions & 1 deletion stats.json

This file was deleted.

6 changes: 5 additions & 1 deletion tsconfig.node.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
"include": [
"mocks/",
"vite.config.ts",
"vite-plugin-serve-handler/",
]
}
Loading

0 comments on commit d7aa555

Please sign in to comment.