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
|
nofollow: true
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<script async type="module" src="/assets/api.js"></script>
|
||||||
|
|
||||||
Here's your options for receiving updates about new blog posts:
|
Here's your options for receiving updates about new blog posts:
|
||||||
|
|
||||||
## Option 1: Email
|
## Option 1: Email
|
||||||
@ -40,82 +42,45 @@ is published then input your email below and smash that subscribe button!
|
|||||||
<span id="emailStatus"></span>
|
<span id="emailStatus"></span>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const emailAddress = document.getElementById("emailAddress");
|
const emailAddress = document.getElementById("emailAddress");
|
||||||
const emailSubscribe = document.getElementById("emailSubscribe");
|
const emailSubscribe = document.getElementById("emailSubscribe");
|
||||||
const emailSubscribeOrigValue = emailSubscribe.value;
|
const emailSubscribeOrigValue = emailSubscribe.value;
|
||||||
const emailStatus = document.getElementById("emailStatus");
|
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 () => {
|
emailSubscribe.onclick = async () => {
|
||||||
|
|
||||||
|
const api = await import("/assets/api.js");
|
||||||
|
|
||||||
emailSubscribe.disabled = true;
|
emailSubscribe.disabled = true;
|
||||||
emailSubscribe.className = "";
|
emailSubscribe.className = "";
|
||||||
emailSubscribe.value = "Please hold...";
|
emailSubscribe.value = "Please hold...";
|
||||||
|
emailStatus.innerHTML = '';
|
||||||
|
|
||||||
await (async () => {
|
try {
|
||||||
|
|
||||||
setErr = (errStr, retry) => {
|
|
||||||
emailStatus.className = "fail";
|
|
||||||
emailStatus.innerHTML = errStr
|
|
||||||
if (retry) emailStatus.innerHTML += " (please try again)";
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!window.isSecureContext) {
|
if (!window.isSecureContext) {
|
||||||
setErr("The browser environment is not secure.", false);
|
throw "The browser environment is not secure.";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPowReq = new Request('/api/pow/challenge');
|
await api.call('POST', '/api/mailinglist/subscribe', {
|
||||||
const res = await fetch(getPowReq)
|
body: { email: emailAddress.value },
|
||||||
.then(response => response.json())
|
requiresPow: true,
|
||||||
|
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const subRes = await fetch(subscribeReq)
|
|
||||||
.then(response => response.json());
|
|
||||||
|
|
||||||
if (subRes.error) {
|
|
||||||
setErr(subRes.error, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emailStatus.className = "success";
|
emailStatus.className = "success";
|
||||||
emailStatus.innerHTML = "Verification email sent (check your spam folder)";
|
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>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user