2023-04-21 18:36:06 +02:00
"use strict" ;
function makeFieldset ( legend ) {
let fieldset = document . createElement ( 'fieldset' ) ;
let fieldsetlegend = document . createElement ( 'legend' ) ;
fieldsetlegend . innerHTML = legend ;
fieldset . appendChild ( fieldsetlegend ) ;
return fieldset ;
}
class L3Section {
constructor ( legend , ... inputs ) {
this . legend = legend ;
this . inputs = inputs ;
}
node ( ) {
let fs = makeFieldset ( this . legend ) ;
let sep = undefined ;
for ( const input of this . inputs ) {
2023-04-22 13:53:32 +02:00
fs . append ( input . node ( ) ) ;
2023-04-21 18:36:06 +02:00
}
return fs ;
}
values ( ) {
this . inputs . map ( ( input ) => { input . id = input . value ; } )
}
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
}
console . log ( "options:" , options )
var compiledopts = { } ;
for ( const option of options ) {
if ( ! compiledopts [ option . optionName ] ) {
compiledopts [ option . optionName ] = [ ] ;
}
compiledopts [ option . optionName ] . push ( option . value )
}
console . log ( "compiledopts:" , compiledopts )
let optstrs = [ ] ;
for ( const opt in compiledopts ) {
const values = compiledopts [ opt ] ;
console . log ( "values:" , values ) ;
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 {
constructor ( legend , handleUpdate , template ) {
this . legend = legend ;
this . template = template ;
this . handleUpdate = handleUpdate ;
this . sections = [ ] ;
this . nsections = 0 ;
}
node ( ) {
2023-04-22 13:53:32 +02:00
let fieldset = document . createElement ( 'fieldset' ) ;
2023-04-22 10:33:33 +02:00
let fieldsetlegend = document . createElement ( 'legend' ) ;
let fieldsetbutton = document . createElement ( 'button' ) ;
fieldsetbutton . setAttribute ( 'type' , 'button' ) ;
console . log ( this . template ) ;
2023-04-22 13:53:32 +02:00
fieldsetbutton . addEventListener ( 'click' , ( ) => this . addSection ( fieldset ) ) ;
2023-04-22 10:33:33 +02:00
fieldsetbutton . innerHTML = this . legend ;
fieldsetlegend . appendChild ( fieldsetbutton ) ;
2023-04-22 13:53:32 +02:00
fieldset . appendChild ( fieldsetlegend ) ;
return fieldset ;
2023-04-22 10:33:33 +02:00
}
2023-04-22 13:53:32 +02:00
addSection ( fieldset ) {
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 ( ) ;
for ( let input of nodes . getElementsByTagName ( 'input' ) ) {
console . log ( input ) ;
input . addEventListener ( 'input' , ( ) => this . handleUpdate ( ) )
}
let del = document . createElement ( 'button' ) ;
del . setAttribute ( 'type' , 'button' ) ;
del . addEventListener ( 'click' , ( ) => this . removeSection ( newsection , nodes ) ) ;
del . innerHTML = 'Remove' ;
nodes . append ( del ) ;
2023-04-22 13:53:32 +02:00
fieldset . before ( nodes ) ;
2023-04-22 10:33:33 +02:00
this . handleUpdate ( ) ;
}
removeSection ( del , nodes ) {
console . log ( this . sections ) ;
this . sections = this . sections . filter ( section => section != del ) ;
console . log ( this . sections ) ;
nodes . remove ( ) ;
this . handleUpdate ( ) ;
}
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 ( ) {
2023-04-23 00:19:35 +02:00
let inner = [ ]
2023-04-22 17:53:55 +02:00
let newLabel = undefined
if ( this . label ) {
newLabel = document . createElement ( 'label' ) ;
newLabel . setAttribute ( 'for' , this . id ) ;
newLabel . innerHTML = this . label ;
2023-04-23 00:19:35 +02:00
inner . push ( newLabel )
2023-04-22 17:53:55 +02:00
}
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 ] ) ;
}
2023-04-23 00:19:35 +02:00
inner . push ( 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 ) ;
}
2023-04-22 13:53:32 +02:00
inner . push ( datalist ) ;
2023-04-21 18:36:06 +02:00
}
2023-04-22 13:53:32 +02:00
let div = document . createElement ( 'div' )
div . append ( ... inner ) ;
return div ;
2023-04-21 18:36:06 +02:00
}
id ( ) {
return this . id ;
}
value ( ) {
return this . input . value ;
}
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 ,
} ;
console . log ( "option:" , ret , this . optionName , this . input . value ) ;
return ret ;
}
render ( ) {
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 ;
}
return ` option ${ this . optionName } ' ${ this . input . value } ' `
}
}
2023-04-22 13:53:32 +02:00
class L3MultiInput {
constructor ( label , handleUpdate , template ) {
this . label = label ;
this . template = template ;
this . handleUpdate = handleUpdate ;
this . inputs = [ ] ;
this . ninputs = 0 ;
}
node ( ) {
let inputcontainer = document . createElement ( 'div' ) ;
let inputbutton = document . createElement ( 'button' ) ;
inputbutton . setAttribute ( 'type' , 'button' ) ;
console . log ( this . template ) ;
inputbutton . addEventListener ( 'click' , ( ) => this . addInput ( inputcontainer ) ) ;
inputbutton . innerHTML = this . label ;
inputcontainer . appendChild ( inputbutton ) ;
return inputcontainer ;
}
addInput ( inputcontainer ) {
const idsuffix = ` - ${ this . ninputs ++ } ` ;
let newinput = this . template ( idsuffix ) ;
this . inputs . push ( newinput ) ;
let div = newinput . node ( ) ;
for ( let input of div . getElementsByTagName ( 'input' ) ) {
console . log ( input ) ;
2023-04-22 17:53:55 +02:00
input . addEventListener ( 'input' , ( ) => this . handleUpdate ( ) ) ;
input . addEventListener ( 'focusout' , ( ) => {
if ( input . value ) return ;
2023-04-22 13:53:32 +02:00
2023-04-22 17:53:55 +02:00
this . removeInput ( input , input . parentElement )
this . handleUpdate ( ) ;
} ) ;
}
2023-04-22 13:53:32 +02:00
inputcontainer . before ( div ) ;
this . handleUpdate ( ) ;
}
removeInput ( del , div ) {
this . inputs = this . inputs . filter ( input => input != del ) ;
div . remove ( ) ;
this . handleUpdate ( ) ;
}
render ( ) {
return this . inputs . map ( input => input . render ( ) ) . filter ( input => ! ! input )
}
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 ( ) {
console . log ( 'here' ) ;
renderConfig ( this . sections . flatMap ( section => section . render ( ) ) . filter ( section => ! ! section ) ) ;
}
node ( ) {
let nodes = this . sections . map ( section => section . node ( ) )
for ( let node of nodes ) {
for ( let input of node . getElementsByTagName ( 'input' ) ) {
input . addEventListener ( 'input' , ( ) => this . handleUpdate ( ) )
}
}
return nodes ;
}
}
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-23 00:19:49 +02:00
new L3MultiInput ( '+ Router IPv4 Address' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
return new L3Input ( undefined , 'gatewayRouterIP4' + idsuffix , 'router_ip' , { type : 'search' , maxlength : 15 , placeholder : 'Router IPv4 Address' } ) ;
} ) ,
new L3MultiInput ( '+ Router IPv6 Address' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
return new L3Input ( undefined , 'gatewayRouterIP6' + idsuffix , 'router_ip6' , { type : 'text' , maxlength : 39 , placeholder : 'Router IPv6 Address' } ) ;
} ) ,
2023-04-22 10:33:33 +02:00
) ) ;
l3cfg . addSection ( new L3Section ( 'dns' ,
new L3Input ( 'Anycast DNS' , 'dnsAnycast' , 'server' , { type : 'radio' , name : 'anycast' , value : 'fd43:5602:29bd:ffff:1:1:1:1' , checked : '' } ) ,
new L3Input ( 'Anycast DNS64' , 'dnsAnycast64' , 'server' , { type : 'radio' , name : 'anycast' , value : 'fd43:5602:29bd:ffff:1:1:1:64' } ) ,
new L3Input ( 'Disable' , 'dnsAnycastNone' , 'server' , { type : 'radio' , name : 'anycast' , value : undefined } ) ,
2023-04-22 13:53:32 +02:00
new L3MultiInput ( '+ Custom DNS Server' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
2023-04-22 17:53:55 +02:00
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, ...' } ) ,
new L3MultiInput ( '+ Client IPv6 Subnet' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
return new L3Input ( undefined , 'clientIP6Addr' + idsuffix , 'ip6addr' , { type : 'text' , maxlength : 39 , placeholder : 'IPv6 CIDR, 2a0b:f4c0:XX:YYYY::/64, fd43:5602:29bd:XXXX::/64,...' } ) ;
} ) ,
new L3MultiInput ( '+ Client IPv4 Subnet' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
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-22 10:33:33 +02:00
l3cfg . addSection ( new L3MultiSection ( '+ vlan' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
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, ...' } ) ,
) ;
} ) ) ;
l3cfg . addSection ( new L3MultiSection ( '+ babelpeer' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
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-21 18:36:06 +02:00
new L3Input ( 'type' , 'babelpeerType' , 'type' , { type : 'search' , placeholder : 'wired, wireless, ...' } , [ 'wired' , '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, ...' } ) ,
) ;
} ) ) ;
l3cfg . addSection ( new L3MultiSection ( '+ wireguardpeer' , ( ) => l3cfg . handleUpdate ( ) , function ( idsuffix ) {
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-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 ? ` \t option ${ 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 ( ) ;