generalize api call logic and move it to a module
This commit is contained in:
parent
1608ca7425
commit
5746a510fc
73
static/src/assets/api.js
Normal file
73
static/src/assets/api.js
Normal 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,
|
||||
}
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user