<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nathan Summers</title>
    <description>The latest articles on DEV Community by Nathan Summers (@ncolesummers).</description>
    <link>https://dev.to/ncolesummers</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F537287%2Fa70c7229-4abe-4c78-9eec-19b45e095ade.jpeg</url>
      <title>DEV Community: Nathan Summers</title>
      <link>https://dev.to/ncolesummers</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ncolesummers"/>
    <language>en</language>
    <item>
      <title>Building a Configuration Generator for Mikrotiks Using Wails and React</title>
      <dc:creator>Nathan Summers</dc:creator>
      <pubDate>Fri, 16 Apr 2021 04:43:34 +0000</pubDate>
      <link>https://dev.to/ncolesummers/building-a-configuration-generator-for-mikrotiks-using-wails-and-react-2gg6</link>
      <guid>https://dev.to/ncolesummers/building-a-configuration-generator-for-mikrotiks-using-wails-and-react-2gg6</guid>
      <description>&lt;p&gt;I work at an Internet Service Provider (ISP) in Idaho, and we use &lt;a href="https://mikrotik.com" rel="noopener noreferrer"&gt;Mikrotiks&lt;/a&gt; extensively for fiber and fixed wireless deployments.  Configuring routers by hand was a common cause of errors on our network.  The high number of unique configurations drastically increased the troubleshooting complexity of support calls.&lt;/p&gt;

&lt;p&gt;This project began as a simple python script to automate the on-premises configuration of every Mikrotik that we deploy.  The project requirements kept increasing until the terminal application became unwieldy and confusing.  The script's primary users have little terminal experience, so it was not the best medium for them.  The wireless technicians are frequently in areas without internet or cellular reception, so a web app would not be possible.  I started development of a simple desktop application that replaced the terminal application.&lt;/p&gt;

&lt;p&gt;Switching to the compiled language "Go" proved to be an easy migration from the scripting and compiling with pyinstaller in the old terminal solution.   And, learning to code in Go was not difficult. Go's templating library made generating the configurations a breeze.  Using the new "embed" package allowed including all of my templates directly into the final binary. Here's an example template that adds a DHCP Client to ether1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{define "dhcpClient"}}
### DHCP Client ###
{
/ip dhcp-client add interface=ether1 use-peer-dns=yes add-default-route=yes dhcp-options=hostname,clientid disabled=no
/log info message=“DHCP client Configured”
}
{{- end}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before attempting to build a solution in "Wails," I created a GUI using &lt;a href="https://fyne.io" rel="noopener noreferrer"&gt;Fyne&lt;/a&gt;.  Fyne was easy to build with, and I could make all of the desktop components using Go.  Unfortunately, The legacy laptops I have to support don't have a recent graphics driver that would work with OpenGL, so I had to find another solution.  &lt;a href="https://wails.app" rel="noopener noreferrer"&gt;Wails&lt;/a&gt; is that solution.  Wails is a cross-platform desktop application framework that uses a web-view and web technologies to create a User Interface (UI).  Now I can use &lt;a href="https://reactjs.org" rel="noopener noreferrer"&gt;React&lt;/a&gt;, the most popular framework for building UIs, and not rely on Go's fledgling GUI  support.  The fact that Wails uses mshtml, a win32 API that hasn't seen an update since Internet Explorer version 11 (IE11), was a feature in my case.&lt;/p&gt;

&lt;p&gt;Building with Wails is as simple as binding a function to the front-end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuildRouter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And calling the function using Javascript/Typescript.  In the below example, I'm passing a Javascript object, which gets converted to &lt;code&gt;map[string]interface{}&lt;/code&gt; on the Go side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;myRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Installation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedInstall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;DisableWiFi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disableWiFi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;SSID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;WPA2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wpa2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Bridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bridged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;LTE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BuildRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myRouter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After passing the map to the backend, it is converted to a struct, as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Router&lt;/span&gt;

  &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mapstructure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;decoding&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The struct is then passed to the corresponding template to be executed, where it writes the executed template to the clipboard and a file in the current directory.  &lt;/p&gt;

&lt;p&gt;The UI is a simple form with two radio groups for selections, a row of checkboxes, and three to five input fields for the installer to fill in, depending on the choices selected.  Each field has validation, and the generate button is enabled when all of the visible fields are valid. Clicking the generate button passes the form data to the backend, as shown above.  The installer then pastes the configuration file into the Mikrotik through a terminal and lets the configuration script work its magic.       &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwn6kw12bgfhvey1nhqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwn6kw12bgfhvey1nhqz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wails made it simple to create a front-end for my application. I'd be willing to use it again for desktop development.  Although, for future projects, I am considering another path using &lt;a href="https://tauri.studio" rel="noopener noreferrer"&gt;Tauri&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>go</category>
      <category>react</category>
      <category>wails</category>
    </item>
  </channel>
</rss>
