diff options
Diffstat (limited to 'src/assets/js/main.js')
| -rw-r--r-- | src/assets/js/main.js | 1117 |
1 files changed, 1117 insertions, 0 deletions
diff --git a/src/assets/js/main.js b/src/assets/js/main.js new file mode 100644 index 0000000..ef57764 --- /dev/null +++ b/src/assets/js/main.js @@ -0,0 +1,1117 @@ +var iface = 'wg0'; + +function parse_placeholder(val) { + return val; +} + +function parse_boolean(val) { + if (val === 'true') + return true; + if (val === 'false') + return false; + throw new Error('Cannot parse as a boolean.'); +} + +function parse_boolean_or_string(val) { + if (typeof val == 'boolean') { + return val; + } + return parse_boolean(val); +} + +function try_parse_boolean_or_string(val) { + try { + return parse_boolean_or_string(val); + } catch (err) { + return null; + } +} + +function parse_positive_integer(src) { + var val = parseInt(src); + if (isNaN(val) || val < 1) + throw new Error('Cannot parse as a positive integer.'); + return val; +} + +function parse_keepalive(src) { + return parse_positive_integer(src); +} + +function parse_tunnel_everything(src) { + return parse_boolean_or_string(src); +} + +function parse_endpoint(val) { + val = val.trim(); + if (val.length == 0) + throw new Error('Server endpoint cannot be an empty string.'); + if (!val.match(/^.+:[0-9]+$/)) + throw new Error('Please specify a host and a port in the HOST:PORT format.'); + return val; +} + +function parse_key(val) { + val = val.trim(); + if (val.length == 0) + throw new Error('Key as used by WireGuard cannot be an empty string.'); + if (!val.match(/^[0-9a-zA-Z+/=]+$/)) + throw new Error('Key as used by WireGuard must be Base64-encoded.'); + return val; +} + +var ip_address_lib = require('ip-address'); + +function parse_ip_internal(src, parser) { + src = src.trim(); + if (src.length == 0) + throw new Error('IP address cannot be an empty string.'); + + try { + var result = new parser(src); + } catch (err) { + throw new Error('Sorry, IP address validation failed with the following error:\n' + err.message); + } + return result; +} + +function parse_ip(src, parser) { + var result = parse_ip_internal(src, parser); + + if (result.parsedSubnet) + throw new Error('Please specify address without the netmask.') + + return result.correctForm(); +} + +function parse_ip_cidr(src, parser) { + var result = parse_ip_internal(src, parser); + + if (!result.parsedSubnet) + throw new Error('Please specify address using the CIDR format (including the netmask).'); + + var addr = result.correctForm(); + var netmask = result.subnetMask; + var network_id = result.startAddress().correctForm(); + + return [addr, netmask, network_id]; +} + +function parse_ipv4(src) { + var result = parse_ip(src, ip_address_lib.Address4); + return result; +} + +function parse_ipv4_cidr(src) { + var parser = ip_address_lib.Address4; + var result = parse_ip_cidr(src, ip_address_lib.Address4); + + var addr = result[0]; + var netmask = result[1]; + var network_id = result[2]; + var broadcast_addr = new parser(src).endAddress().correctForm(); + + if (addr == network_id && netmask != 32) + throw new Error('Please use a real host IP address, not a network identifier.'); + if (addr == broadcast_addr && netmask != 32) + throw new Error('Please use a real host IP address, not a broadcast address.'); + + return result; +} + +function parse_ipv6(val) { + return parse_ip(val, ip_address_lib.Address6); +} + +function parse_ipv6_cidr(val) { + return parse_ip_cidr(val, ip_address_lib.Address6); +} + +function parse_ip_list(val, addr_parser) { + var parts = val.split(/, */); + var result = []; + parts.forEach(function(part) { + result.push(addr_parser(part)); + }); + return result; +} + +var Input = function(name) { + this.name = name; + + if (this.is_checkbox()) { + // Keep the checked property and the value consistent. + $(this.input_id()).change(function(evt) { + var elem = evt.target; + elem.value = elem.checked; + }); + } +} + +Input.prototype.input_id = function() { + return '#param_' + this.name; +} + +Input.prototype.error_id = function() { + return this.input_id() + '_error'; +} + +Input.prototype.container_id = function() { + return this.input_id() + '_container'; +} + +Input.prototype.is_checkbox = function() { + return $(this.input_id()).attr('type') == 'checkbox'; +} + +Input.prototype.value = function() { + return $(this.input_id()).val(); +} + +Input.prototype.has_value = function() { + return this.value().length > 0; +} + +Input.prototype.set = function(value) { + if (this.is_checkbox() && try_parse_boolean_or_string(value)) + $(this.input_id()).prop('checked', true); + $(this.input_id()).val(value); +} + +Input.prototype.show = function() { + $(this.container_id()).show(); +} + +Input.prototype.hide = function() { + $(this.container_id()).hide(); +} + +Input.prototype.show_error = function(msg) { + this.show(); + $(this.error_id()).text(msg); + $(this.error_id()).show(); +} + +Input.prototype.hide_error = function() { + $(this.error_id()).hide(); +} + +var Value = function(name, parser) { + this.name = name; + this.input = new Input(name); + this.parser = parser; + this.optional = false; + this.advanced = false; + + this.value = null; + this.error = null; +} + +Value.prototype.parse = function() { + return this.parse_from(this.input.value()); +} + +Value.prototype.parse_from = function(src) { + try { + var value = src; + switch (typeof value) { + case 'boolean': + value = this.parser(value); + break; + default: + if (this.optional && value.length == 0) + break; + value = this.parser(value); + break; + } + this.input.set(src); + this.set_value(value); + return true; + } catch (err) { + this.set_error(err.message); + return false; + } +} + +Value.prototype.set_value = function(value) { + this.value = value; + this.error = null; + this.input.hide_error(); +} + +Value.prototype.set_error = function(msg) { + this.value = null; + this.error = msg; + this.input.show_error(this.error); +} + +Value.prototype.is_set = function() { + return this.input.has_value(); +} + +Value.prototype.show = function() { + this.input.show(); +} + +Value.prototype.hide = function() { + this.input.hide(); +} + +var Endpoint = function(name) { + Value.call(this, name, parse_endpoint); +} + +Endpoint.prototype = Object.create(Value.prototype); +Endpoint.prototype.constructor = Endpoint; + +var Key = function(name) { + Value.call(this, name, parse_key); +} + +Key.prototype = Object.create(Value.prototype); +Key.prototype.constructor = Key; + +var IPAddr = function(name, parser) { + Value.call(this, name, parser); +} + +IPAddr.prototype = Object.create(Value.prototype); +IPAddr.prototype.constructor = IPAddr; + +IPAddr.prototype.set_value = function(value) { + Value.prototype.set_value.call(this, value); + + this.addr = this.value[0]; + this.netmask = this.value[1]; + this.network_id = this.value[2]; +} + +IPAddr.prototype.full_address = function() { + return this.addr + '/' + this.netmask; +} + +IPAddr.prototype.allowed_ips_client = function() { + // As of this writing, _every_ tool accepts something like 192.168.0.1/24 + // here, _except_ for the Android app, which _requires_ the least + // significant bits to be zeroed out. + return this.network_id + '/' + this.netmask; +} + +var IPv4 = function(name) { + IPAddr.call(this, name, parse_ipv4_cidr); +} + +IPv4.prototype = Object.create(IPAddr.prototype); +IPv4.prototype.constructor = IPv4; + +IPv4.prototype.allowed_ips_server = function() { + return this.addr + '/' + 32; +} + +var IPv6 = function(name) { + IPAddr.call(this, name, parse_ipv6_cidr); +} + +IPv6.prototype = Object.create(IPAddr.prototype); +IPv6.prototype.constructor = IPv6; + +IPv6.prototype.allowed_ips_server = function() { + return this.addr + '/' + 128; +} + +var Keepalive = function(name) { + Value.call(this, name, parse_keepalive); + this.optional = true; + this.advanced = true; +} + +Keepalive.prototype = Object.create(Value.prototype); +Keepalive.prototype.constructor = Keepalive; + +var TunnelEverything = function(name) { + Value.call(this, name, parse_tunnel_everything); + this.optional = true; + this.advanced = true; +} + +TunnelEverything.prototype = Object.create(Value.prototype); +TunnelEverything.prototype.constructor = TunnelEverything; + +var IPAddrList = function(name, parser) { + Value.call(this, name, function(val) { + return parse_ip_list(val, parser); + }); +} + +IPAddrList.prototype = Object.create(Value.prototype); +IPAddrList.prototype.constructor = IPAddrList; + +var DNS_IPv4 = function(name) { + IPAddrList.call(this, name, parse_ipv4); + this.optional = true; + this.advanced = true; +} + +DNS_IPv4.prototype = Object.create(IPAddrList.prototype); +DNS_IPv4.prototype.constructor = DNS_IPv4; + +var DNS_IPv6 = function(name) { + IPAddrList.call(this, name, parse_ipv6); + this.optional = true; + this.advanced = true; +} + +DNS_IPv6.prototype = Object.create(IPAddrList.prototype); +DNS_IPv6.prototype.constructor = DNS_IPv6; + +var Data = function() { + this.server_public = new Key('server_public'); + this.server_endpoint = new Endpoint('server_endpoint'); + this.preshared = new Key('preshared'); + this.client_public = new Key('client_public'); + this.client_private = new Key('client_private'); + this.client_ipv4 = new IPv4('client_ipv4'); + this.client_ipv6 = new IPv6('client_ipv6'); + this.keepalive = new Keepalive('keepalive'); + this.tunnel_everything = new TunnelEverything('tunnel_everything'); + this.dns_ipv4 = new DNS_IPv4('dns_ipv4'); + this.dns_ipv6 = new DNS_IPv6('dns_ipv6'); + + this.values = [ + this.server_public, + this.server_endpoint, + this.preshared, + this.client_public, + this.client_private, + this.client_ipv4, + this.client_ipv6, + this.keepalive, + this.tunnel_everything, + this.dns_ipv4, + this.dns_ipv6 + ]; +} + +Data.prototype.set_from_url = function(url) { + var url = new URL(url); + var params = new URLSearchParams(url.search); + var has = false; + + this.values.forEach(function(value) { + if (params.has(value.name)) { + has = true; + value.input.set(params.get(value.name)); + } + }); + + return has; +} + +Data.prototype.set_from_this_url = function() { + return this.set_from_url(window.location.href); +}; + +Data.prototype.get_perma_url = function() { + var url = new URL(window.location.href); + url.search = ''; + var params = new URLSearchParams(); + this.values.forEach(function(value) { + params.append(value.name, value.input.value()); + }); + url.search = params.toString(); + return url.toString(); +} + +Data.prototype.parse = function() { + var success = true; + + this.values.forEach(function(value) { + if (!value.parse()) + success = false; + }); + + if (success) { + if (!$('#advanced_params').prop('checked')) + this.hide_advanced(); + this.hide_error(); + } else { + this.show_error(); + } + return success; +} + +Data.prototype.show_error = function() { + $('#params_error').text('Please correct the input errors above first.'); + $('#params_error').show(); +} + +Data.prototype.hide_error = function() { + $('#params_error').hide(); +} + +Data.prototype.show_advanced = function() { + this.values.forEach(function(value) { + if (!value.advanced) + return; + value.show(); + }); +} + +Data.prototype.hide_advanced = function() { + this.values.forEach(function(value) { + if (!value.advanced) + return; + value.hide(); + }); +} + +function basename(path) { + return path.substring(path.lastIndexOf('/') + 1); +} + +function download(file_name, text) { + // The type must be application/octet-stream; if it's text/plain, the + // Android Chrome appends the .txt suffix to the downloaded file names. + var blob = new Blob([text], {type: 'application/octet-stream'}); + var url = URL.createObjectURL(blob); + + var link = $('<a/>'); + link.prop('href', url); + link.prop('download', file_name); + + // Whoever thought of this [0] is fucking crazy: + // https://stackoverflow.com/a/36483380/514684 + // It literally silently does nothing if this is omitted. + link[0].click(); +} + +function copy_to_clipboard(elem) { + // This is rather neat, thanks: + // https://stackoverflow.com/a/22581382/514684 + + // Apparently, you can only do execCommand('copy') from <input> and + // <textarea> elements. Another stupidity, I'm so surprised. + var is_input = elem.tagName === 'INPUT' || elem.tagName === 'TEXTAREA'; + var orig_select_start, orig_select_end; + + if (is_input) { + var target = elem; + orig_select_start = elem.selectionStart; + orig_select_end = elem.selectionEnd; + } else { + var target = document.createElement('textarea'); + target.textContent = elem.textContent; + document.body.appendChild(target); + } + + var current_focus = document.activeElement; + target.focus(); + target.setSelectionRange(0, target.value.length); + + var succeed; + try { + // Supposedly, this is deprecated. But the "proper" method sucks balls, + // so I'm using this one: + // https://stackoverflow.com/a/30810322/514684 + succeed = document.execCommand('copy'); + } catch (e) { + succeed = false; + } + + if (current_focus && typeof current_focus.focus === "function") { + current_focus.focus(); + } + + if (is_input) { + elem.setSelectionRange(orig_select_start, orig_select_end); + } else { + document.body.removeChild(target); + } + return succeed; +} + +var OverlayButton = function(title, icon, click_handler) { + this.btn = $('<button/>', { + 'class': 'btn btn-default', + type: 'button', + on: { + click: click_handler + } + }); + this.title = title; + this.icon = icon; + this.show_icon(title, icon); +} + +OverlayButton.prototype.show_icon = function(title, icon) { + this.btn.empty(); + this.btn.prop('title', title); + this.btn.append($('<span/>', { + 'class': `glyphicon glyphicon-${icon}` + })); +} + +var EditButton = function(cfg) { + var self = this; + OverlayButton.call(this, 'Edit', 'pencil', function(evt) { + return self.on_click(cfg); + }); +} + +EditButton.prototype = Object.create(OverlayButton.prototype); +EditButton.prototype.constructor = EditButton; + +EditButton.prototype.on_click = function(cfg) { + var editable = cfg.pre.prop('isContentEditable'); + cfg.pre.prop('contentEditable', !editable); + if (editable) { + this.show_icon(this.title, this.icon); + this.btn.blur(); // a.k.a. unfocus + } else { + this.show_icon('Save', 'floppy-disk'); + cfg.pre.focus(); + } +} + +var DownloadButton = function(cfg) { + var self = this; + OverlayButton.call(this, 'Download', 'download-alt', function(evt) { + return self.on_click(cfg); + }); +} + +DownloadButton.prototype = Object.create(OverlayButton.prototype); +DownloadButton.prototype.constructor = DownloadButton; + +DownloadButton.prototype.on_click = function(cfg) { + return download(basename(cfg.name), cfg.pre.text()); +} + +var CopyButton = function(cfg) { + var self = this; + OverlayButton.call(this, 'Copy', 'copy', function(evt) { + return self.on_click(cfg); + }); +} + +CopyButton.prototype = Object.create(OverlayButton.prototype); +CopyButton.prototype.constructor = CopyButton; + +CopyButton.prototype.on_click = function(cfg) { + return copy_to_clipboard(cfg.pre[0]); +} + +var ConfigFile = function(name, text) { + this.name = name; + this.text = text; + this.pre = $('<pre/>').text(text); +} + +ConfigFile.prototype.format = function() { + var buttons = [ + new CopyButton(this), + new DownloadButton(this), + new EditButton(this) + ]; + + var overlay = $('<div class="pre_buttons btn-group btn-group-xs" role="group"/>'); + buttons.forEach(function(btn) { + overlay.append(btn.btn); + }); + + return $('<div class="pre_container"/>') + .append(this.pre) + .append(overlay); +} + +var QRCode = function(pre) { + this.pre = pre; +} + +function draw_qr_code(container, text) { + var canvas = $('<canvas/>'); + var qr = new QRious({ + element: canvas[0], + value: text, + size: 350 + }); + container.append(canvas); +} + +QRCode.prototype.format = function() { + var container = $('<div class="text-center"/>'); + var pre = this.pre; + pre.on('blur', function() { + container.empty(); + draw_qr_code(container, pre.text()); + }); + draw_qr_code(container, pre.text()); + return container; +} + +var catchall_ipv4 = '0.0.0.0/0'; +var catchall_ipv6 = '::/0'; + +function wg_quick_client_file(data) { + var path = `/etc/wireguard/${iface}.conf`; + + var dns = ''; + var allowed_ips = `${data.client_ipv4.allowed_ips_client()}, ${data.client_ipv6.allowed_ips_client()}`; + var keepalive = ''; + + if (data.tunnel_everything.value) { + dns = `DNS = ${(data.dns_ipv4.value.concat(data.dns_ipv6.value)).join(',')}\n`; + allowed_ips = `${catchall_ipv4}, ${catchall_ipv6}`; + } + if (data.keepalive.is_set()) { + keepalive = `PersistentKeepalive = ${data.keepalive.value}\n`; + } + + var contents = +`# On the client, put this to +# ${path} +# and either +# * start the wg-quick@${iface} systemd service, +# * or run \`wg-quick up ${iface}\`. + +[Interface] +PrivateKey = ${data.client_private.value} +Address = ${data.client_ipv4.full_address()}, ${data.client_ipv6.full_address()} +${dns} +[Peer] +Endpoint = ${data.server_endpoint.value} +PublicKey = ${data.server_public.value} +PresharedKey = ${data.preshared.value} +AllowedIPs = ${allowed_ips} +${keepalive}`; + + return new ConfigFile(path, contents); +} + +function wg_quick_server_file(data) { + var path = `/etc/wireguard/${iface}.conf`; + + var contents = +`# On the server, add this to +# ${path} +# and restart the wg-quick@${iface} systemd service. + +# Previous contents goes here... + +[Peer] +PublicKey = ${data.client_public.value} +PresharedKey = ${data.preshared.value} +AllowedIPs = ${data.client_ipv4.allowed_ips_server()}, ${data.client_ipv6.allowed_ips_server()} +`; + + return new ConfigFile(path, contents); +} + +function systemd_client_netdev_file(data) { + var path = `/etc/systemd/network/${iface}.netdev`; + + var fwmark = ''; + var allowed_ips = `${data.client_ipv4.allowed_ips_client()}, ${data.client_ipv6.allowed_ips_client()}`; + var keepalive = ''; + + if (data.tunnel_everything.value) { + fwmark = `FirewallMark = 0x8888\n`; + allowed_ips = `${catchall_ipv4}, ${catchall_ipv6}`; + } + if (data.keepalive.is_set()) { + keepalive = `PersistentKeepalive = ${data.keepalive.value}\n`; + } + + var contents = +`# On the client, you need two files. Put this into +# ${path} +# and after you're done with both files, run +# systemctl daemon-reload +# systemctl restart systemd-networkd + +[NetDev] +Name = ${iface} +Kind = wireguard + +[WireGuard] +PrivateKey = ${data.client_private.value} +${fwmark} +[WireGuardPeer] +Endpoint = ${data.server_endpoint.value} +PublicKey = ${data.server_public.value} +PresharedKey = ${data.preshared.value} +AllowedIPs = ${allowed_ips} +${keepalive}`; + + return new ConfigFile(path, contents); +} + +function systemd_client_network_file(data) { + var path = `/etc/systemd/network/${iface}.network`; + + var dns = ''; + + if (data.tunnel_everything.value) { + data.dns_ipv4.value.forEach(function(val) { + dns += `DNS = ${val}\n`; + }); + data.dns_ipv6.value.forEach(function(val) { + dns += `DNS = ${val}\n`; + }); + dns += +`Domains = ~. +DNSDefaultRoute = true + +[RoutingPolicyRule] +FirewallMark = 0x8888 +InvertRule = true +Table = 1000 +Priority = 10 +`; + } + + var contents = +`# This is the second file. Put this into +# ${path} +# and if you're done with the first file already, run +# systemctl daemon-reload +# systemctl restart systemd-networkd + +[Match] +Name = ${iface} + +[Network] +Address = ${data.client_ipv4.full_address()} +Address = ${data.client_ipv6.full_address()} +${dns}`; + + // I know the above doesn't work, but I don't care, systemd-networkd is a + // fucking nightmare. + return new ConfigFile(path, contents); +} + +function systemd_server_netdev_file(data) { + var path = `/etc/systemd/network/${iface}.netdev`; + + var contents = +`# On the server, add this to +# ${path} +# and run +# systemctl daemon-reload +# systemctl restart systemd-networkd + +# Previous contents goes here... + +[WireGuardPeer] +PublicKey = ${data.client_public.value} +PresharedKey = ${data.preshared.value} +AllowedIPs = ${data.client_ipv4.allowed_ips_server()}, ${data.client_ipv6.allowed_ips_server()} +`; + + return new ConfigFile(path, contents); +} + +function nmcli_client_file(data) { + var path = `/etc/NetworkManager/system-connections/${iface}.nmconnection`; + + var allowed_ips = `${data.client_ipv4.allowed_ips_client()};${data.client_ipv6.allowed_ips_client()};`; + var dns4 = ''; + var dns6 = ''; + var keepalive = ''; + + if (data.tunnel_everything.value) { + allowed_ips = `${catchall_ipv4};${catchall_ipv6};`; + dns4 = +`dns=${data.dns_ipv4.value.join(';')}; +dns-priority=-10 +dns-search=~; +`; + dns6 = +`dns=${data.dns_ipv6.value.join(';')}; +dns-priority=-10 +dns-search=~; +`; + } + if (data.keepalive.is_set()) { + keepalive = `persistent-keepalive=${data.keepalive.value}\n`; + } + + var contents = +`# On the client, put this to +# ${path} +# and run +# chmod 0600 ${path} +# nmcli c reload +# nmcli c up ${iface} + +[connection] +id=${iface} +type=wireguard +interface-name=${iface} + +[wireguard] +private-key=${data.client_private.value} +private-key-flags=0 + +[wireguard-peer.${data.server_public.value}] +endpoint=${data.server_endpoint.value} +preshared-key=${data.preshared.value} +preshared-key-flags=0 +allowed-ips=${allowed_ips} +${keepalive} +[ipv4] +address1=${data.client_ipv4.full_address()} +method=manual +${dns4} +[ipv6] +addr-gen-mode=stable-privacy +address1=${data.client_ipv6.full_address()} +method=manual +${dns6}`; + + return new ConfigFile(path, contents); +} + +function nmcli_server_file(data) { + var path = `/etc/NetworkManager/system-connections/${iface}.nmconnection`; + + var contents = +`# On the server, add this to +# ${path} +# and run +# nmcli c reload +# nmcli c up ${iface} + +# Previous contents goes here... + +[wireguard-peer.${data.client_public.value}] +preshared-key=${data.preshared.value} +preshared-key-flags=0 +allowed-ips=${data.client_ipv4.allowed_ips_server()};${data.client_ipv6.allowed_ips_server()}; +`; + + return new ConfigFile(path, contents); +} + +var shell_info = +`# Make sure your .bash_history or whatever is +# secure before running this. +# Better yet, put into a file and run (possibly, using sudo).`; + +function manual_client_script(data) { + var path = 'client_setup.sh'; + + var allowed_ips = `${data.client_ipv4.allowed_ips_client()},${data.client_ipv6.allowed_ips_client()}`; + var keepalive = ''; + + if (data.tunnel_everything.value) { + allowed_ips = `${catchall_ipv4},${catchall_ipv6}`; + } + if (data.keepalive.is_set()) { + keepalive = ` persistent-keepalive ${data.keepalive.value} \\\n`; + } + + var contents = +`# On the client, run this to set up a connection. +${shell_info} + +ip link add dev ${iface} type wireguard +ip addr add ${data.client_ipv4.full_address()} dev ${iface} +ip addr add ${data.client_ipv6.full_address()} dev ${iface} +wg set ${iface} \\ + private-key <( echo ${data.client_private.value} ) +wg set ${iface} \\ + peer ${data.server_public.value} \\ + preshared-key <( echo ${data.preshared.value} ) \\ +${keepalive} endpoint ${data.server_endpoint.value} \\ + allowed-ips ${allowed_ips} +ip link set ${iface} up +`; + + return new ConfigFile(path, contents); +} + +function manual_server_script(data) { + var path = 'server_setup.sh'; + + var contents = +`# On the server, run this to tweak an existing connection. +${shell_info} + +wg set ${iface} \\ + peer ${data.client_public.value} \\ + preshared-key <( echo ${data.preshared.value} ) \\ + allowed-ips ${data.client_ipv4.allowed_ips_server()},${data.client_ipv6.allowed_ips_server()} +`; + + return new ConfigFile(path, contents); +} + +var Guide = function(name) { + this.name = name; +} + +Guide.prototype.format = function(data) { + var container = $('<div/>'); + container.append($('<h2/>').html(this.name)); + return container; +} + +var GuidePermaURL = function() { + Guide.call(this, 'Permanent URL'); +} + +GuidePermaURL.prototype = Object.create(Guide.prototype); +GuidePermaURL.prototype.constructor = GuidePermaURL; + +GuidePermaURL.prototype.format = function(data) { + var container = Guide.prototype.format.call(this, data); + container + .append($('<p/>').text('Use the following URL to share this configuration. Be careful: it probably contains sensitive data, like your private keys.')) + .append($('<pre/>').append($('<a/>', {href: data.get_perma_url()}).text(data.get_perma_url()))); + return container; +} + +var GuideServerClient = function(name) { + Guide.call(this, name); +} + +GuideServerClient.prototype = Object.create(Guide.prototype); +GuideServerClient.prototype.constructor = GuideServerClient; + +GuideServerClient.prototype.for_server = function(data) { return []; } +GuideServerClient.prototype.for_client = function(data) { return []; } + +GuideServerClient.prototype.join_guides = function(guides) { + var container = $('<div/>'); + guides.forEach(function(guide) { + container.append(guide.format()); + }); + return container; +} + +GuideServerClient.prototype.format = function(data) { + var container = Guide.prototype.format.call(this, data); + container + .append($('<div class="row"/>') + .append($('<div class="col-md-6"/>') + .append(this.join_guides(this.for_server(data)))) + .append($('<div class="col-md-6"/>') + .append(this.join_guides(this.for_client(data))))); + return container; +} + +var GuideWgQuick = function() { + GuideServerClient.call(this, 'wg-quick'); +} + +GuideWgQuick.prototype = Object.create(GuideServerClient.prototype); +GuideWgQuick.prototype.constructor = GuideWgQuick; + +GuideWgQuick.prototype.for_client = function(data) { + var config = wg_quick_client_file(data); + var qr = new QRCode(config.pre); + return [config, qr]; +} + +GuideWgQuick.prototype.for_server = function(data) { + return [wg_quick_server_file(data)]; +} + +var GuideSystemd = function() { + GuideServerClient.call(this, 'systemd-networkd'); +} + +GuideSystemd.prototype = Object.create(GuideServerClient.prototype); +GuideSystemd.prototype.constructor = GuideSystemd; + +GuideSystemd.prototype.for_client = function(data) { + return [systemd_client_netdev_file(data), systemd_client_network_file(data)]; +} + +GuideSystemd.prototype.for_server = function(data) { + return [systemd_server_netdev_file(data)]; +} + +var GuideNetworkManager = function() { + GuideServerClient.call(this, 'NetworkManager'); +} + +GuideNetworkManager.prototype = Object.create(GuideServerClient.prototype); +GuideNetworkManager.prototype.constructor = GuideNetworkManager; + +GuideNetworkManager.prototype.for_client = function(data) { + return [nmcli_client_file(data)]; +} + +GuideNetworkManager.prototype.for_server = function(data) { + return [nmcli_server_file(data)]; +} + +var GuideManual = function() { + GuideServerClient.call(this, '<code>ip</code> & <code>wg</code>'); +} + +GuideManual.prototype = Object.create(GuideServerClient.prototype); +GuideManual.prototype.constructor = GuideManual; + +GuideManual.prototype.for_client = function(data) { + return [manual_client_script(data)]; +} + +GuideManual.prototype.for_server = function(data) { + return [manual_server_script(data)]; +} + +function clear_guides() { + $('#guides').empty(); +} + +function add_guide(guide, data) { + $('#guides').append(guide.format(data)); +} + +function guides_from_data(data) { + if (!data.parse()) { + return; + } + + var guides = [ + new GuidePermaURL(), + new GuideWgQuick(), + new GuideNetworkManager(), + new GuideSystemd(), + new GuideManual() + ]; + + guides.forEach(function(guide) { + add_guide(guide, data); + }); +} + +function form_on_submit() { + clear_guides(); + + var data = new Data(); + guides_from_data(data); + + return false; +} + +function advanced_params_on_click(input) { + var data = new Data(); + if (input.checked) + data.show_advanced(); + else + data.hide_advanced(); +} + +function main() { + var data = new Data(); + if (data.set_from_this_url()) + guides_from_data(data); +} + +$(function() { + main(); +}); |
