mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-15 19:30:38 +08:00
We remove a couple of calls to `console.error()` in the code for the local node connection. It’s not necessary to log errors that are expected, for example, if the node is down. Signed-off-by: Thomas Scholtes <geigerzaehler@axiom.fm>
176 lines
4.3 KiB
TypeScript
176 lines
4.3 KiB
TypeScript
import { derived, get, writable } from "svelte/store";
|
|
|
|
import { HttpdClient } from "@httpd-client";
|
|
import { withTimeout, Mutex, E_CANCELED, E_TIMEOUT } from "async-mutex";
|
|
|
|
export interface Session {
|
|
id: string;
|
|
publicKey: string;
|
|
}
|
|
|
|
export type HttpdState =
|
|
| { state: "stopped" }
|
|
| { state: "running" }
|
|
| {
|
|
state: "authenticated";
|
|
session: Session;
|
|
};
|
|
|
|
const HTTPD_STATE_STORAGE_KEY = "httpdState";
|
|
const HTTPD_CUSTOM_PORT_KEY = "httpdCustomPort";
|
|
|
|
const store = writable<HttpdState>({ state: "stopped" });
|
|
export const httpdStore = derived(store, s => s);
|
|
|
|
export const api = new HttpdClient({
|
|
hostname: "127.0.0.1",
|
|
port: 8080,
|
|
scheme: "http",
|
|
});
|
|
|
|
let pollHttpdStateHandle: number | undefined = undefined;
|
|
|
|
export function changeHttpdPort(port: number) {
|
|
window.localStorage.setItem(HTTPD_CUSTOM_PORT_KEY, String(port));
|
|
void checkState();
|
|
}
|
|
|
|
function update(state: HttpdState) {
|
|
window.localStorage.setItem(HTTPD_STATE_STORAGE_KEY, JSON.stringify(state));
|
|
store.set(state);
|
|
}
|
|
|
|
const stateMutex = withTimeout(new Mutex(), 5_000);
|
|
|
|
export async function authenticate(params: {
|
|
id: string;
|
|
signature: string;
|
|
publicKey: string;
|
|
}): Promise<boolean> {
|
|
stateMutex.cancel();
|
|
return stateMutex.runExclusive(async () => {
|
|
try {
|
|
await api.session.update(params.id, {
|
|
sig: params.signature,
|
|
pk: params.publicKey,
|
|
});
|
|
update({
|
|
state: "authenticated",
|
|
session: { id: params.id, publicKey: params.publicKey },
|
|
});
|
|
return true;
|
|
} catch (error) {
|
|
console.error(error);
|
|
update({ state: "stopped" });
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
export async function disconnect() {
|
|
stateMutex.cancel();
|
|
await stateMutex
|
|
.runExclusive(async () => {
|
|
const httpd = get(store);
|
|
if (httpd.state !== "authenticated") {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await api.session.delete(httpd.session.id);
|
|
update({ state: "running" });
|
|
} catch (error) {
|
|
console.error(error);
|
|
update({ state: "stopped" });
|
|
}
|
|
})
|
|
.catch(error => {
|
|
if (error !== E_CANCELED) {
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
|
|
async function checkState() {
|
|
let httpdState: HttpdState | null = null;
|
|
const rawHttpdState = window.localStorage.getItem(HTTPD_STATE_STORAGE_KEY);
|
|
const customHttpdPort = window.localStorage.getItem(HTTPD_CUSTOM_PORT_KEY);
|
|
if (customHttpdPort) {
|
|
api.changePort(Number(customHttpdPort));
|
|
}
|
|
if (rawHttpdState) {
|
|
try {
|
|
httpdState = JSON.parse(rawHttpdState);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
await stateMutex
|
|
.runExclusive(async () => {
|
|
try {
|
|
if (httpdState && httpdState.state === "authenticated") {
|
|
const sess = await api.session.getById(httpdState.session.id);
|
|
const unixTimeInSeconds = Math.floor(Date.now() / 1000);
|
|
if (
|
|
sess.status === "unauthorized" ||
|
|
sess.expiresAt < unixTimeInSeconds
|
|
) {
|
|
update({ state: "running" });
|
|
} else {
|
|
update(httpdState);
|
|
}
|
|
} else {
|
|
await api.getNode();
|
|
update({ state: "running" });
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof TypeError && error.message !== "Failed to fetch") {
|
|
console.error(error);
|
|
}
|
|
update({ state: "stopped" });
|
|
}
|
|
})
|
|
.catch(error => {
|
|
if (error !== E_CANCELED && error !== E_TIMEOUT) {
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
|
|
function pollSession() {
|
|
if (pollHttpdStateHandle) {
|
|
return;
|
|
}
|
|
|
|
pollHttpdStateHandle = window.setInterval(() => checkState(), 10_000);
|
|
}
|
|
|
|
export function initialize() {
|
|
// Sync session state changes with other open tabs and windows.
|
|
addEventListener("storage", event => {
|
|
if (
|
|
(event.key === HTTPD_STATE_STORAGE_KEY &&
|
|
event.oldValue !== event.newValue) ||
|
|
(event.key === HTTPD_CUSTOM_PORT_KEY && event.oldValue !== event.newValue)
|
|
) {
|
|
void checkState();
|
|
}
|
|
});
|
|
|
|
void checkState();
|
|
|
|
// Properly clean up setInterval and restart session polling when Vite
|
|
// performs hot module reload on file changes.
|
|
if (import.meta.hot) {
|
|
import.meta.hot.accept();
|
|
import.meta.hot.dispose(() => {
|
|
clearInterval(pollHttpdStateHandle);
|
|
pollHttpdStateHandle = undefined;
|
|
pollSession();
|
|
});
|
|
}
|
|
|
|
pollSession();
|
|
}
|