summaryrefslogtreecommitdiff
path: root/master/server/src/server.ts
blob: 5774a2bcba86e79089bbf332355eb54913865cab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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 local_address = nw_interface_info.address;

	const port = 80;

	const address = is_prod ? local_address : "localhost";

	await app.listen(port, address);

	console.log(`Listening on address http://${address}:${port}`);
}

main();