Skip to content

Commit

Permalink
Refactor web frontend and remove unnecessary dependencies.
Browse files Browse the repository at this point in the history
- Eliminate frontend JS build step completely:
  (remove dep: NodeJS, npx, tailwind, postcss, autoprefixer)

- Declutter and cleanup index.html (8.49 KB to 3.4 KB) = ~60% savings.

- Replace tailwind with custom CSS (10.64 KB to 1.96 KB) = ~81% savings.

- Remove Google font (~100 KB) as there is very little text on the page.

- Refactor and cleanup main.js and remove tailwind styling logic.
  (2.82 KB to 1.12 KB) = ~60% savings.

- Net static asset reduction = 21.95 KB to 6.48 KB = ~70% savings
  apart from the 100+ KB elimination of Google fonts.
  • Loading branch information
knadh authored and mr-karan committed Mar 10, 2021
1 parent 0dc61ac commit b8ba782
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 1,433 deletions.
10 changes: 1 addition & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,10 @@ VERSION := ${HASH}
build-cli:
go build -o ${CLI_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/cli/

.PHONY: build-frontend
build-frontend:
cd cmd/doggo/api && NODE_ENV=production npx tailwindcss build -o ./assets/tailwind.css

.PHONY: build-api
build-api:
go build -o ${API_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/api/

.PHONY: deps
deps:
cd cmd/doggo/api && npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

.PHONY: build
build: build-api build-cli

Expand All @@ -29,7 +21,7 @@ run-cli: build-cli ## Build and Execute the CLI binary after the build step.
${CLI_BIN}

.PHONY: run-api
run-api: build-frontend build-api ## Build and Execute the API binary after the build step.
run-api: build-api ## Build and Execute the API binary after the build step.
${API_BIN} --config config-api-sample.toml

.PHONY: clean
Expand Down
180 changes: 58 additions & 122 deletions cmd/doggo/api/assets/main.js
Original file line number Diff line number Diff line change
@@ -1,154 +1,90 @@
const $ = document.querySelector.bind(document);
const $new = document.createElement.bind(document);
const apiURL = "/api/lookup/";
const isMobile = window.matchMedia("only screen and (max-width: 760px)").matches;

function handleNSChange() {
if ($('select[name=ns]').value == "custom") {
$('div[id=custom_ns]').classList.remove("hidden");
$('div[id=ns]').classList.add("hidden");
} else {
$('div[id=custom_ns]').classList.add("hidden");
$('div[id=ns]').classList.remove("hidden");
$('input[name=ns]').placeholder = $('select[name=ns]').value;
}
}


// Source: https://stackoverflow.com/a/1026087.
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

window.addEventListener('DOMContentLoaded', (event) => {
handleNSChange();
});
const $show = (el) => {
el.classList.remove('hidden');
};
const $hide = (el) => {
el.classList.add('hidden');
};

const apiURL = '/api/lookup/';

(function () {
const fields = ['name', 'address', 'type', 'ttl', 'rtt', 'nameserver'];
const fields = ['name', 'address', 'type', 'ttl', 'rtt'];

// createRow creates a table row with the given cell values.
function createRow(item) {
const tr = $new('tr');
fields.forEach((f) => {
const td = $new('td');
td.classList.add("px-6", "py-4", "whitespace-nowrap", "text-sm");
if (f == "ttl" || f == "rtt" || f == "nameserver") {
td.classList.add("text-gray-500");
} else {
td.classList.add("text-gray-900");
}
if (f == "name") {
td.classList.add("font-semibold");
}
if (f == "type") {
td.innerHTML = '<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">' + item[f] + '</span>';
} else {
td.innerText = item[f];
}
td.innerText = item[f];
td.classList.add(f);
tr.appendChild(td);
});
return tr;
}

const handleSubmit = async () => {
const tbody = $('#table tbody'),
tbl = $('#table');
tbody.innerHTML = '';
$hide(tbl);

// `createList` creates a table row with the given cell values.
function createList(item) {
const ul = $new('ul');
ul.classList.add("m-4", "block", "bg-indigo-100");
fields.forEach((f) => {
const li = $new('li');
const span = $new('span');
span.classList.add("p-2", "text-gray-500", "font-semibold");
span.innerText = capitalizeFirstLetter(f) + ': ' + item[f]
li.appendChild(span);
ul.appendChild(li);
});
return ul;
}

function prepareNSAddr(ns) {
switch (ns) {
// If it's a custom nameserver, get the value from the user's input.
case "custom":
return $('input[name=custom_ns]').value.trim()
// Else get it from the select dropdown field.
default:
return $('select[name=ns]').value.trim()
}
}

const postForm = body => {
return fetch(apiURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body
});
};

const handleSubmit = async (e) => {
e.preventDefault();

const tbl = $('table tbody');
const list = $('div[id=mobile-answers-sec]');
tbl.innerHTML = '';
list.innerHTML = '';

$('p[id=empty-name-sec]').classList.add("hidden");
const errSec = $('div[id=api-error-sec]');
errSec.classList.add("hidden");

const q = $('input[name=q]').value.trim(), typ = $('select[name=type]').value;
const ns = $('select[name=ns]').value;

if (!q) {
$('p[id=empty-name-sec]').classList.remove("hidden");
throw ('Invalid query name.');
}
const q = $('input[name=q]').value.trim(),
typ = $('select[name=type]').value,
addr = $('input[name=address]').value.trim();

if (!q || !typ || !ns) {
throw ('Invalid or empty query params.');
}
// Post to the API.
const req = await fetch(apiURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: [q,], type: [typ,], nameservers: [addr,] })
});

const nsAddr = prepareNSAddr(ns);
const body = JSON.stringify({ query: [q,], type: [typ,], nameservers: [nsAddr,] });
const res = await req.json();

const response = await postForm(body);
const res = await response.json();
if (res.status != "success") {
// get error message from body or default to response statusText
if (res.status != 'success') {
const error = (res && res.message) || response.statusText;
errSec.classList.remove("hidden");
errSec.innerHTML = '<p class="text-xl text-red-500">' + error + '</p>'
throw (error);
throw(error);
return;
}

if (res.data[0].answers == null) {
const errSec = $('div[id=api-error-sec]');
errSec.classList.remove("hidden");
errSec.innerHTML = '<p class="text-xl text-red-500">' + 'No records found!' + '</p>'
return null;
throw('No records found.');
return;
}

$('div[id=answer_sec]').classList.remove("hidden");
res.data[0].answers.forEach((item) => {
tbody.appendChild(createRow(item));
});

if (isMobile === true) {
list.classList.remove("hidden");
res.data[0].answers.forEach((item) => {
console.log("appending", item)
list.appendChild(createList(item));
});
$show(tbl);
};

} else {
res.data[0].answers.forEach((item) => {
tbl.appendChild(createRow(item));
});
}
// Capture the form submit.
$('#form').onsubmit = async (e) => {
e.preventDefault();

const msg = $('#message');
$hide(msg);

try {
await handleSubmit();
} catch(e) {
msg.innerText = e.toString();
$show(msg);
throw e;
}
};

document.querySelector('form').addEventListener('submit', handleSubmit);
// Change the address on ns change.
const ns = $("#ns"), addr = $("#address");
addr.value = ns.value;

ns.onchange = (e) => {
addr.value = e.target.value;
if(addr.value === "") {
addr.focus();
}
};
})();

0 comments on commit b8ba782

Please sign in to comment.