generalize api call logic and move it to a module

This commit is contained in:
Brian Picciano 2021-08-29 21:34:24 -06:00
parent 1608ca7425
commit 5746a510fc
2 changed files with 93 additions and 55 deletions

73
static/src/assets/api.js Normal file
View File

@ -0,0 +1,73 @@
const doFetch = async (req) => {
let res, jsonRes;
try {
res = await fetch(req);
jsonRes = await res.json();
} catch (e) {
if (e instanceof SyntaxError)
e = new Error(`status ${res.status}, empty (or invalid) response body`);
console.error(`api call ${req.method} ${req.url}: unexpected error:`, e);
throw e;
}
if (jsonRes.error) {
console.error(
`api call ${req.method} ${req.url}: application error:`,
res.status,
jsonRes.error,
);
throw jsonRes.error;
}
return jsonRes;
}
// may throw
const solvePow = async () => {
const res = await call('GET', '/api/pow/challenge');
const worker = new Worker('/assets/solvePow.js');
const p = new Promise((resolve, reject) => {
worker.postMessage({seedHex: res.seed, target: res.target});
worker.onmessage = resolve;
});
const powSol = (await p).data;
worker.terminate();
return {seed: res.seed, solution: powSol};
}
const call = async (method, route, opts = {}) => {
const { body = {}, requiresPow = false } = opts;
const reqOpts = { method };
if (requiresPow) {
const {seed, solution} = await solvePow();
body.powSeed = seed;
body.powSolution = solution;
}
if (Object.keys(body).length > 0) {
const form = new FormData();
for (const key in body) form.append(key, body[key]);
reqOpts.body = form;
}
const req = new Request(route, reqOpts);
return doFetch(req);
}
export {
call,
}

View File

@ -4,6 +4,8 @@ title: "Follow the Blog"
nofollow: true
---
<script async type="module" src="/assets/api.js"></script>
Here's your options for receiving updates about new blog posts:
## Option 1: Email
@ -40,82 +42,45 @@ is published then input your email below and smash that subscribe button!
<span id="emailStatus"></span>
<script>
const emailAddress = document.getElementById("emailAddress");
const emailSubscribe = document.getElementById("emailSubscribe");
const emailSubscribeOrigValue = emailSubscribe.value;
const emailStatus = document.getElementById("emailStatus");
const solvePow = async (seedHex, target) => {
const worker = new Worker('/assets/solvePow.js');
const p = new Promise((resolve, reject) => {
worker.postMessage({seedHex, target});
worker.onmessage = resolve;
});
const solutionHex = (await p).data;
worker.terminate();
return solutionHex;
}
emailSubscribe.onclick = async () => {
const api = await import("/assets/api.js");
emailSubscribe.disabled = true;
emailSubscribe.className = "";
emailSubscribe.value = "Please hold...";
emailStatus.innerHTML = '';
await (async () => {
setErr = (errStr, retry) => {
emailStatus.className = "fail";
emailStatus.innerHTML = errStr
if (retry) emailStatus.innerHTML += " (please try again)";
};
try {
if (!window.isSecureContext) {
setErr("The browser environment is not secure.", false);
return;
throw "The browser environment is not secure.";
}
const getPowReq = new Request('/api/pow/challenge');
const res = await fetch(getPowReq)
.then(response => response.json())
if (res.error) {
setErr(res.error, true);
return;
}
const powSol = await solvePow(res.seed, res.target);
const subscribeForm = new FormData();
subscribeForm.append('powSeed', res.seed);
subscribeForm.append('powSolution', powSol);
subscribeForm.append('email', emailAddress.value);
const subscribeReq = new Request('/api/mailinglist/subscribe', {
method: 'POST',
body: subscribeForm,
await api.call('POST', '/api/mailinglist/subscribe', {
body: { email: emailAddress.value },
requiresPow: true,
});
const subRes = await fetch(subscribeReq)
.then(response => response.json());
if (subRes.error) {
setErr(subRes.error, true);
return;
}
emailStatus.className = "success";
emailStatus.innerHTML = "Verification email sent (check your spam folder)";
})();
} catch (e) {
emailStatus.className = "fail";
emailStatus.innerHTML = e;
} finally {
emailSubscribe.disabled = false;
emailSubscribe.className = "button-primary";
emailSubscribe.value = emailSubscribeOrigValue;
}
emailSubscribe.disabled = false;
emailSubscribe.className = "button-primary";
emailSubscribe.value = emailSubscribeOrigValue;
};
</script>