summaryrefslogtreecommitdiff
path: root/master/server/src/server.ts
diff options
context:
space:
mode:
Diffstat (limited to 'master/server/src/server.ts')
-rw-r--r--master/server/src/server.ts173
1 files changed, 173 insertions, 0 deletions
diff --git a/master/server/src/server.ts b/master/server/src/server.ts
new file mode 100644
index 0000000..4368b78
--- /dev/null
+++ b/master/server/src/server.ts
@@ -0,0 +1,173 @@
+import { get_network_interfaces, ping_subnetwork } from "networking";
+
+import { exit, argv } from "process";
+import { readdir } from "fs/promises";
+import { join } from "path";
+import { NetworkInterfaceBase } from "os";
+
+import { fastify } from "fastify";
+import fastify_static from "@fastify/static";
+import {
+ toIP as mac_to_ip_address
+} from "@network-utils/arp-lookup";
+import { IPMask, IPv4 } from "ip-matching";
+
+const minion_mac_addresses = [
+ "30:83:98:a4:68:09",
+ "bc:ff:4d:17:62:75"
+];
+
+const minion_positions: Record<string, number> = {
+ "30:83:98:a4:68:09": 13,
+ "bc:ff:4d:17:62:75": 1
+};
+
+async function mac_to_ip_addresses(mac_addresses: string[]) {
+ let failed = [];
+
+ let succeeded = [];
+
+ for(const mac_address of mac_addresses) {
+ const ip_address = await mac_to_ip_address(mac_address);
+
+ if(ip_address === null) {
+ failed.push(mac_address);
+ continue;
+ }
+
+ succeeded.push({ mac_address: mac_address, ip_address: ip_address });
+ }
+
+ return {
+ succeeded: succeeded,
+ failed: failed
+ };
+}
+
+interface NWInterfaceInfo extends NetworkInterfaceBase {
+ family: number | string
+ scopeid?: number;
+}
+
+function get_ipv4_nw_interface_info() {
+ const nw_interfaces = get_network_interfaces();
+
+ const nw_interface_name = Object.keys(nw_interfaces)[0];
+
+ const nw_interface_info_arr = nw_interfaces[nw_interface_name] as NWInterfaceInfo[];
+
+ const nw_interface_info = nw_interface_info_arr.find(info => {
+ return info.family === "IPv4" || info.family === 4;
+ });
+
+ if(nw_interface_info === undefined) {
+ console.error(
+ `Error: Network interface "${nw_interface_name}" doesn't support IPv4"`
+ );
+ exit(1);
+ }
+
+ return nw_interface_info;
+}
+
+function get_current_subnetwork(nw_interface_info: NWInterfaceInfo) {
+ const ip_mask = new IPMask(
+ new IPv4(nw_interface_info.address),
+ new IPv4(nw_interface_info.netmask)
+ );
+
+ const subnetwork = ip_mask.convertToSubnet();
+
+ if(subnetwork === undefined) {
+ console.error(`Error: Failed to convert ${ip_mask.input} to subnet`);
+ exit(1);
+ }
+
+ return subnetwork;
+}
+
+async function get_minion_ip_addresses(nw_interface_info: NWInterfaceInfo) {
+ let mac_to_ip_result = await mac_to_ip_addresses(minion_mac_addresses);
+
+ if(mac_to_ip_result.failed.length === 0) {
+ return mac_to_ip_result.succeeded;
+ }
+
+ console.log(
+ "Warning: Couldn't find all minion IP addresses in ARP table. " +
+ "Updating ARP table entries..."
+ );
+
+ const subnetwork = get_current_subnetwork(nw_interface_info);
+
+ await ping_subnetwork(subnetwork);
+
+ console.log("Retrying finding minion IP addresses in ARP table...");
+
+ mac_to_ip_result = await mac_to_ip_addresses(minion_mac_addresses);
+
+ if(mac_to_ip_result.failed.length !== 0) {
+ console.error(
+ "Error: Failed to find all minion IP addresses in ARP table " +
+ "even after updating ARP table entries"
+ );
+ exit(1);
+ }
+
+ return mac_to_ip_result.succeeded;
+}
+
+async function main() {
+ const nw_interface_info = get_ipv4_nw_interface_info();
+
+ const minion_info = (await get_minion_ip_addresses(nw_interface_info)).map(
+ ({ mac_address, ip_address }) => {
+ return {
+ ip_address: ip_address,
+ position: minion_positions[mac_address]
+ };
+ }
+ );
+
+ const app = fastify();
+
+ const is_prod = argv.length !== 0 && argv.includes("--prod");
+
+ if(is_prod) {
+ const dist_dir_path = join(__dirname, "/../../client/dist");
+
+ await readdir(dist_dir_path).catch(() => {
+ console.error("Error: The client dist directory doesn't exist");
+ exit(1);
+ });
+
+ app.register(fastify_static, { root: dist_dir_path });
+
+ app.route({
+ method: "GET",
+ url: "/",
+ handler: (req, reply) => {
+ reply.sendFile("index.html");
+ }
+ });
+
+ }
+
+ app.route({
+ method: "GET",
+ url: "/api/v1/minions",
+ handler: (req, reply) => {
+ reply.send({ data: minion_info });
+ }
+ });
+
+ const address = nw_interface_info.address;
+
+ const port = 80;
+
+ await app.listen(port, address);
+
+ console.log(`Listening on address http://${address}:${port}`);
+}
+
+main();