summaryrefslogtreecommitdiff
path: root/ui/lib
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2025-04-23 22:19:24 -0400
committerOwen Jacobson <owen@grimoire.ca>2025-04-23 22:19:24 -0400
commit713afd0b4963460cc37b3767c6b542c8c828d227 (patch)
tree5156b6874cdd3cde684b4719d5e8fe1500b47612 /ui/lib
parent1ebe642ebe7c9ba72eb1713ec21f0cd03949a47f (diff)
parent4d6cc62f0c9bb5d50720c83fb1ecbd0889d2acd3 (diff)
Retry boot every five seconds.
Merged in spite of misgivings. This method will loop over the request until it completes, even if the user moves to a view where the response is no longer relevant.
Diffstat (limited to 'ui/lib')
-rw-r--r--ui/lib/apiServer.js61
-rw-r--r--ui/lib/retry.js21
-rw-r--r--ui/lib/session.svelte.js24
3 files changed, 79 insertions, 27 deletions
diff --git a/ui/lib/apiServer.js b/ui/lib/apiServer.js
index 0b51883..cad8997 100644
--- a/ui/lib/apiServer.js
+++ b/ui/lib/apiServer.js
@@ -1,56 +1,59 @@
import axios from 'axios';
+import * as r from './retry.js';
+import { timedDelay } from './retry.js';
+
export const apiServer = axios.create({
- baseURL: '/api/',
- validateStatus: () => true
+ baseURL: '/api/'
});
export async function boot() {
- return apiServer.get('/boot');
+ return apiServer.get('/boot').catch(responseError);
}
export async function setup(name, password) {
- return apiServer.post('/setup', { name, password });
+ return await apiServer.post('/setup', { name, password }).catch(responseError);
}
export async function logIn(name, password) {
- return apiServer.post('/auth/login', { name, password });
+ return await apiServer.post('/auth/login', { name, password }).catch(responseError);
}
export async function logOut() {
- return apiServer.post('/auth/logout', {});
+ return await apiServer.post('/auth/logout', {}).catch(responseError);
}
export async function changePassword(password, to) {
- return apiServer.post('/password', { password, to });
+ return await apiServer.post('/password', { password, to }).catch(responseError);
}
export async function createChannel(name) {
- return apiServer.post('/channels', { name });
+ return await apiServer.post('/channels', { name }).catch(responseError);
}
export async function postToChannel(channelId, body) {
- return apiServer.post(`/channels/${channelId}`, { body });
+ return await apiServer.post(`/channels/${channelId}`, { body }).catch(responseError);
}
export async function deleteMessage(messageId) {
- return apiServer.delete(`/messages/${messageId}`, {});
+ return await apiServer.delete(`/messages/${messageId}`, {}).catch(responseError);
}
export async function createInvite() {
- return apiServer.post(`/invite`, {});
+ return await apiServer.post(`/invite`, {}).catch(responseError);
}
export async function getInvite(inviteId) {
- return apiServer.get(`/invite/${inviteId}`);
+ return await apiServer.get(`/invite/${inviteId}`).catch(responseError);
}
export async function acceptInvite(inviteId, name, password) {
- const data = {
- name,
- password
- };
- return apiServer.post(`/invite/${inviteId}`, data);
+ return apiServer
+ .post(`/invite/${inviteId}`, {
+ name,
+ password
+ })
+ .catch(responseError);
}
export function subscribeToEvents(resumePoint) {
@@ -62,3 +65,27 @@ export function subscribeToEvents(resumePoint) {
});
return new EventSource(eventsUrl);
}
+
+export class LoggedOut extends Error {}
+
+export class SetupRequired extends Error {}
+
+export async function retry(op) {
+ return await r.retry(op, isRetryable, r.timedDelay(5000));
+}
+
+function responseError(err) {
+ if (err.response) {
+ switch (err.response.status) {
+ case 401:
+ throw new LoggedOut();
+ case 503:
+ throw new SetupRequired();
+ }
+ }
+ throw err;
+}
+
+function isRetryable(err) {
+ return !!err.request;
+}
diff --git a/ui/lib/retry.js b/ui/lib/retry.js
new file mode 100644
index 0000000..a2cff65
--- /dev/null
+++ b/ui/lib/retry.js
@@ -0,0 +1,21 @@
+export async function retry(callback, shouldRetry, delay) {
+ while (true) {
+ try {
+ return await callback();
+ } catch (err) {
+ if (shouldRetry(err)) {
+ await delay();
+ } else {
+ throw err;
+ }
+ }
+ }
+}
+
+export function timedDelay(millis) {
+ return async () => await sleep(millis);
+}
+
+function sleep(millis) {
+ return new Promise((resolve) => setTimeout(resolve, millis));
+}
diff --git a/ui/lib/session.svelte.js b/ui/lib/session.svelte.js
index b953d9c..21a391d 100644
--- a/ui/lib/session.svelte.js
+++ b/ui/lib/session.svelte.js
@@ -94,16 +94,20 @@ class Session {
}
async function bootOrNavigate(navigateTo) {
- const response = await api.boot();
- switch (response.status) {
- case 401:
- await navigateTo('/login');
- break;
- case 503:
- await navigateTo('/setup');
- break;
- case 200:
- return response.data;
+ try {
+ const response = await api.retry(async () => await api.boot());
+ return response.data;
+ } catch (err) {
+ switch (true) {
+ case err instanceof api.LoggedOut:
+ await navigateTo('/login');
+ break;
+ case err instanceof api.SetupRequired:
+ await navigateTo('/setup');
+ break;
+ default:
+ throw err;
+ }
}
}