From ea33f87e590dd6f010590ecfc5d5b07dcabe0c64 Mon Sep 17 00:00:00 2001 From: HampusM Date: Tue, 31 May 2022 00:02:58 +0200 Subject: feat(master): add server package --- master/server/src/server.ts | 173 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 master/server/src/server.ts (limited to 'master/server/src/server.ts') 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 = { + "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(); -- cgit v1.2.3-18-g5258