l3config.js/l3config.js

369 lines
12 KiB
JavaScript
Raw Normal View History

2023-04-21 18:36:06 +02:00
"use strict";
function makeFieldset() {
return document.createElement('fieldset');
}
function makeLegend(...legend) {
let ret = document.createElement('legend');
ret.append(...legend);
return ret;
}
function makeButton(label, onClick) {
let ret = document.createElement('button');
ret.setAttribute('type', 'button');
ret.append(label);
ret.addEventListener('click', onClick);
return ret;
}
function makeText(text) {
return document.createTextNode(text);
2023-04-21 18:36:06 +02:00
}
class L3Section {
constructor(legend, ...inputs) {
this.legend = legend;
this.inputs = inputs;
}
node() {
let fieldset = makeFieldset();
fieldset.append(makeLegend(makeText(this.legend)));
2023-04-21 18:36:06 +02:00
let sep = undefined;
for (const input of this.inputs) {
fieldset.append(input.node());
2023-04-21 18:36:06 +02:00
}
return fieldset;
2023-04-21 18:36:06 +02:00
}
render() {
const options = this.inputs
2023-04-22 13:53:32 +02:00
.flatMap(input => input.option())
2023-04-21 18:36:06 +02:00
.filter(option => !!option);
if (options.length == 0) {
return undefined
}
var compiledopts = {};
for (const option of options) {
if (!compiledopts[option.optionName]) {
compiledopts[option.optionName] = [];
}
compiledopts[option.optionName].push(option.value)
}
let optstrs = [];
for (const opt in compiledopts) {
const values = compiledopts[opt];
if (values.length == 1) {
optstrs.push(`option ${opt} '${values[0]}'`);
} else {
for (const value of values) {
2023-04-22 10:33:33 +02:00
optstrs.push(`list ${opt} '${value}'`);
2023-04-21 18:36:06 +02:00
}
}
}
return `config ${this.legend}\n\t` + optstrs.join('\n\t')
}
}
2023-04-22 10:33:33 +02:00
class L3MultiSection {
2023-04-24 11:05:15 +02:00
constructor(legend, template) {
2023-04-22 10:33:33 +02:00
this.legend = legend;
this.template = template;
this.sections = [];
this.nsections = 0;
}
node() {
let fieldset = makeFieldset();
fieldset.append(
makeLegend(
2023-04-24 11:05:15 +02:00
makeButton(`+ ${this.legend}`, () => { fieldset.before(this.#makeSection()); }),
)
);
2023-04-22 13:53:32 +02:00
return fieldset;
2023-04-22 10:33:33 +02:00
}
#makeSection() {
2023-04-22 10:33:33 +02:00
const idsuffix = `-${this.nsections++}`;
let newsection = this.template(idsuffix);
this.sections.push(newsection);
let nodes = newsection.node();
let del = document.createElement('button');
del.setAttribute('type', 'button');
del.addEventListener('click', () => this.#removeSection(newsection, nodes));
del.classList.add('del');
2023-04-22 10:33:33 +02:00
del.innerHTML = 'Remove';
nodes.append(del);
return nodes;
2023-04-22 10:33:33 +02:00
}
#removeSection(del, nodes) {
2023-04-22 10:33:33 +02:00
this.sections = this.sections.filter(section => section != del);
nodes.remove();
}
render() {
return this.sections.map(section => section.render()).filter(section => !!section)
}
}
2023-04-21 18:36:06 +02:00
class L3Input {
constructor(label, id, optionName, attrs, datalist) {
this.label = label;
this.id = id;
this.optionName = optionName;
this.attrs = attrs;
this.datalist = datalist
this.input = undefined;
}
node() {
let div = document.createElement('div')
if (this.label) {
let newLabel = document.createElement('label');
newLabel.setAttribute('for', this.id);
newLabel.innerHTML = this.label;
div.appendChild(newLabel);
}
2023-04-21 18:36:06 +02:00
this.input = document.createElement('input');
this.input.setAttribute('id', this.id);
for (const attr in this.attrs) {
this.input.setAttribute(attr, this.attrs[attr]);
}
div.appendChild(this.input);
2023-04-21 18:36:06 +02:00
if (this.datalist) {
let datalist = document.createElement('datalist');
let datalistid = this.id + '-datalist';
datalist.setAttribute('id', datalistid);
this.input.setAttribute('list', datalistid);
for (const value of this.datalist) {
let option = document.createElement('option');
option.setAttribute('value', value);
datalist.append(option);
}
div.appendChild(datalist);
2023-04-21 18:36:06 +02:00
}
2023-04-22 13:53:32 +02:00
return div;
2023-04-21 18:36:06 +02:00
}
option() {
switch (this.input.type) {
case 'radio':
2023-04-23 00:19:35 +02:00
case 'checkbox':
2023-04-21 18:36:06 +02:00
if (!this.input.checked || this.input.value == 'undefined') return undefined;
break;
default:
if (!this.input.value) return undefined;
break;
}
const ret = {
optionName: this.optionName,
value: this.input.value,
};
return ret;
}
}
2023-04-24 00:06:38 +02:00
class L3Select {
constructor(label, id, optionName, attrs, optionList) {
this.label = label;
this.id = id;
this.optionName = optionName;
this.attrs = attrs;
this.optionList = optionList;
this.input = undefined;
}
node() {
let div = document.createElement('div')
if (this.label) {
let newLabel = document.createElement('label');
newLabel.setAttribute('for', this.id);
newLabel.innerHTML = this.label;
div.appendChild(newLabel);
}
this.input = document.createElement('select');
this.input.setAttribute('id', this.id);
for (const attr in this.attrs) {
this.input.setAttribute(attr, this.attrs[attr]);
}
div.appendChild(this.input);
for (const [label, value] of this.optionList) {
let option = document.createElement('option');
option.setAttribute('value', value);
option.appendChild(makeText(label));
this.input.appendChild(option);
}
return div;
}
option() {
if (this.input.value == 'undefined') { return undefined; }
const ret = {
optionName: this.optionName,
value: this.input.value,
};
return ret;
}
}
2023-04-22 13:53:32 +02:00
class L3MultiInput {
2023-04-24 11:05:15 +02:00
constructor(label, template) {
2023-04-22 13:53:32 +02:00
this.label = label;
this.template = template;
this.inputs = [];
this.ninputs = 0;
}
node() {
let fieldset = makeFieldset();
fieldset.append(
makeLegend(
2023-04-24 11:05:15 +02:00
makeButton(`+ ${this.label}`, () => { fieldset.append(this.#makeInput()); }),
)
);
return fieldset;
2023-04-22 13:53:32 +02:00
}
#makeInput() {
2023-04-22 13:53:32 +02:00
const idsuffix = `-${this.ninputs++}`;
let newinput = this.template(idsuffix);
this.inputs.push(newinput);
let div = newinput.node();
let delbutton = document.createElement('button');
delbutton.setAttribute('type', 'button');
delbutton.classList.add('del');
delbutton.innerHTML = '-';
2023-04-24 11:05:15 +02:00
delbutton.addEventListener('click', () => { this.#removeInput(newinput, div); });
div.prepend(delbutton);
return div;
2023-04-22 13:53:32 +02:00
}
#removeInput(del, div) {
2023-04-22 13:53:32 +02:00
this.inputs = this.inputs.filter(input => input != del);
div.remove();
}
option() {
return this.inputs.map(input => input.option());
}
}
2023-04-22 10:33:33 +02:00
class L3Config {
constructor() {
this.sections = [];
}
addSection(section) {
this.sections.push(section)
}
handleUpdate() {
renderConfig(this.sections.flatMap(section => section.render()).filter(section => !!section));
}
node() {
2023-04-24 11:05:15 +02:00
return this.sections.map(section => section.node())
2023-04-22 10:33:33 +02:00
}
}
2023-04-21 18:36:06 +02:00
function initForm() {
let l3configinput = document.getElementById('l3configinput');
let form = document.createElement('form');
l3configinput.appendChild(form);
2023-04-22 10:33:33 +02:00
const l3cfg = new L3Config();
l3cfg.addSection(new L3Section('gateway',
2023-04-22 12:22:35 +02:00
new L3Input('Router Name','gatewayName', 'name', {type: 'search', placeholder: 'Router Name'}),
2023-04-24 11:05:15 +02:00
new L3MultiInput('Router IPv4 Address', function(idsuffix) {
2023-04-23 00:19:49 +02:00
return new L3Input(undefined,'gatewayRouterIP4'+idsuffix, 'router_ip', {type: 'search', maxlength: 15, placeholder: 'Router IPv4 Address'});
}),
2023-04-24 11:05:15 +02:00
new L3MultiInput('Router IPv6 Address', function(idsuffix) {
2023-04-23 00:19:49 +02:00
return new L3Input(undefined,'gatewayRouterIP6'+idsuffix, 'router_ip6', {type: 'text', maxlength: 39, placeholder: 'Router IPv6 Address'});
}),
2023-04-24 00:06:38 +02:00
new L3Input('Config Version', 'gatewayConfigVersion', 'config_version', {type: 'text', value: 2, disabled: ''}),
2023-04-22 10:33:33 +02:00
));
l3cfg.addSection(new L3Section('dns',
2023-04-24 00:06:38 +02:00
new L3Select('Preset','dnsAnycastSelect', 'server', {name: 'anycast'}, [
['Anycast DNS', 'fd43:5602:29bd:ffff:1:1:1:1'],
['Anycast DNS64', 'fd43:5602:29bd:ffff:1:1:1:64'],
['---', undefined],
]),
2023-04-24 11:05:15 +02:00
new L3MultiInput('Custom DNS Server', function(idsuffix) {
return new L3Input(undefined,'dnsOther'+idsuffix, 'server', {type: 'text', maxlength: 39, placeholder: 'Custom DNS Server'});
2023-04-22 13:53:32 +02:00
}),
2023-04-22 10:33:33 +02:00
));
l3cfg.addSection(new L3Section('wan',
new L3Input('Use VLAN','babelpeerVLAN', 'vlan', {type: 'number', min: 1, max: 4094}),
new L3Input('Use Interface directly','babelpeerIFACE', 'iface', {type: 'text', placeholder: 'eth0, eth0.4, ...'}),
));
2023-04-23 00:19:49 +02:00
l3cfg.addSection(new L3Section('client',
new L3Input('Use VLAN','clientVLAN', 'vlan', {type: 'number', min: 1, max: 4094}),
new L3Input('Use Interface directly','clientIFACE', 'iface', {type: 'text', placeholder: 'eth0, eth0.4, ...'}),
2023-04-24 11:05:15 +02:00
new L3MultiInput('Client IPv6 Subnet', function(idsuffix) {
2023-04-23 00:19:49 +02:00
return new L3Input(undefined,'clientIP6Addr'+idsuffix, 'ip6addr', {type: 'text', maxlength: 39, placeholder: 'IPv6 CIDR, 2a0b:f4c0:XX:YYYY::/64, fd43:5602:29bd:XXXX::/64,...'});
}),
2023-04-24 11:05:15 +02:00
new L3MultiInput('Client IPv4 Subnet', function(idsuffix) {
2023-04-23 00:19:49 +02:00
return new L3Input(undefined,'clientIPAddr'+idsuffix, 'ipaddr', {type: 'text', maxlength: 15, placeholder: 'IPv4 CIDR, 10.XX.YY.ZZ/24'});
}),
new L3Input('IPv4 SNAT','clientSNAT', 'snat', {type: 'checkbox', value: 1}),
new L3Input('DHCP Start Address','clientDHCPStart', 'dhcp_start', {type: 'text', maxlength: 15, placeholder: 'IPv4 Address, 10.XX.YY.10'}),
new L3Input('DHCP Number of Addresses','clientDHCPLimit', 'dhcp_limit', {type: 'number', max: 65535}),
new L3Input('Wifi ESSID','clientESSID', 'essid', {type: 'text'}),
new L3Input('Wifi 2.4 GHz Channel','client2GHZ', 'chan2ghz', {type: 'number', min: 1, max: 13}),
new L3Input('Wifi 5 GHz Channel','client5GHZ', 'chan5ghz', {type: 'number', min: 36, max: 140}),
));
2023-04-24 11:05:15 +02:00
l3cfg.addSection(new L3MultiSection('VLAN', function(idsuffix) {
2023-04-22 10:33:33 +02:00
return new L3Section('vlan',
new L3Input('Comment','vlanComment', 'comment', {type: 'text'}, ['client', 'wan']),
new L3Input('Ports','vlanPorts', 'ports', {type: 'text', placeholder: 'eth0:*, eth1.4:u, ...'}),
);
}));
2023-04-24 11:05:15 +02:00
l3cfg.addSection(new L3MultiSection('Direct Babel Peering', function(idsuffix) {
2023-04-22 10:33:33 +02:00
return new L3Section('babelpeer',
new L3Input('Use VLAN','babelpeerVLAN', 'vlan', {type: 'number', min: 1, max: 4094}),
new L3Input('Use Interface directly','babelpeerIFACE', 'iface', {type: 'text', placeholder: 'eth0, eth0.4, ...'}),
2023-04-24 00:06:38 +02:00
new L3Select('Babel Interface Type','babelpeerType', 'type', {name: 'type'}, [
['---', undefined],
['Wired', 'wired'],
['Wireless', 'wireless'],
]),
2023-04-22 10:33:33 +02:00
new L3Input('rxcost','babelpeerRXCost', 'rxcost', {type: 'number', min: 96, max: 65535, placeholder: 'LAN: 96, Tunnel: 4096, ...'}),
);
}));
2023-04-24 11:05:15 +02:00
l3cfg.addSection(new L3MultiSection('WireGuard Peering', function(idsuffix) {
2023-04-22 10:33:33 +02:00
return new L3Section('wireguardpeer',
new L3Input('Endpoint Host', 'wireguardpeerEPHost'+idsuffix, 'endpoint_host', {type: 'text', required: ''}, ['peering.nue3.fff.community','ff1.zbau.f3netze.de','nsvm.f3netze.de','2a0b:f4c0:400::']),
2023-04-23 00:19:49 +02:00
new L3Input('Endpoint Port','wireguardpeerEPPort'+idsuffix, 'endpoint_port', {type: 'number', required: '', min:1, max: 65535}),
2023-04-22 10:33:33 +02:00
new L3Input('Persistent Keepalive','wireguardpeerPersistentKeepalive', 'persistent_keepalive', {type: 'number', min: 0, max: 65535, placeholder: '1 - 65535 Seconds...'}),
2023-04-22 13:53:32 +02:00
new L3Input('Remote Public Key', 'wireguardpeerRemotePubKey'+idsuffix, 'remote_public_key', {type: 'text', minlength: 44, maxlength: 44, required: '', placeholder: 'base64 encoded public key'}),
new L3Input('Local Private Key', 'wireguardpeerLocalPrivKey'+idsuffix, 'local_private_key', {type: 'text', minlength: 44, maxlength: 44, placeholder: 'base64 encoded private key'}),
2023-04-22 10:33:33 +02:00
new L3Input('rxcost','wireguardpeerRXCost', 'rxcost', {type: 'number', min: 96, max: 65535, value: 4096, placeholder: '4096'}),
new L3Input('mtu','wireguardpeerMTU', 'mtu', {type: 'number', min: 1280, max: 65535, value: 1412, placeholder: '1412'}),
);
}));
2023-04-21 18:36:06 +02:00
2023-04-24 11:05:15 +02:00
form.addEventListener('input', () => l3cfg.handleUpdate());
2023-04-22 10:33:33 +02:00
form.replaceChildren(...l3cfg.node())
2023-04-22 10:36:39 +02:00
renderConfig(l3cfg.sections.flatMap(section => section.render()).filter(section => !!section));
2023-04-21 18:36:06 +02:00
}
function renderConfigOption(name, value) {
return value ? `\toption ${name} '${value}'\n` : ""
}
function renderConfig(sections) {
let l3configoutput = document.getElementById('l3configoutput');
let l3configoutputpre = document.createElement('pre');
let code = document.createElement('code');
l3configoutputpre.appendChild(code);
code.innerHTML += sections.join('\n\n');
l3configoutput.replaceChildren(l3configoutputpre);
}
initForm();