version importing

This commit is contained in:
DecDuck
2024-10-11 17:16:26 +11:00
parent a7c33e7d43
commit 46c8f0c48a
19 changed files with 587 additions and 113 deletions
+31 -19
View File
@@ -1,3 +1,5 @@
import droplet from "@drop/droplet";
/**
* The TaskHandler setups up two-way connections to web clients and manages the state for them
* This allows long-running tasks (like game imports and such) to report progress, success and error states
@@ -5,18 +7,19 @@
*/
type TaskRegistryEntry = {
runPromise: Promise<void>;
success: boolean;
progress: number;
log: string[];
error: string | undefined;
clients: { [key: string]: boolean };
name: string;
requireAdmin: boolean;
};
class TaskHandler {
private taskRegistry: { [key: string]: TaskRegistryEntry } = {};
private clientRegistry: { [key: string]: PeerImpl } = {};
startTasks: (() => void)[] = [];
constructor() {}
@@ -30,17 +33,18 @@ class TaskHandler {
if (!taskEntry) return;
const taskMessage: TaskMessage = {
id: task.id,
name: task.name,
success: taskEntry.success,
progress: taskEntry.progress,
error: taskEntry.error,
log: taskEntry.log,
log: taskEntry.log.reverse().slice(0, 50),
};
for (const client of Object.keys(taskEntry.clients)) {
if (!this.clientRegistry[client]) continue;
this.clientRegistry[client].send(taskMessage);
}
updateCollectTimeout = undefined;
}, 500);
}, 100);
};
const progress = (progress: number) => {
@@ -57,40 +61,46 @@ class TaskHandler {
updateAllClients();
};
const promiseRun = task.run({ progress, log });
promiseRun.then(() => {
const taskEntry = this.taskRegistry[task.id];
if (!taskEntry) return;
this.taskRegistry[task.id].success = true;
updateAllClients();
});
promiseRun.catch((error) => {
const taskEntry = this.taskRegistry[task.id];
if (!taskEntry) return;
this.taskRegistry[task.id].success = false;
this.taskRegistry[task.id].error = error;
updateAllClients();
});
this.taskRegistry[task.id] = {
name: task.name,
runPromise: promiseRun,
success: false,
progress: 0,
error: undefined,
log: [],
clients: {},
requireAdmin: task.requireAdmin ?? false,
};
droplet.callAltThreadFunc(async () => {
const promiseRun = task.run({ progress, log });
promiseRun.then(() => {
const taskEntry = this.taskRegistry[task.id];
if (!taskEntry) return;
this.taskRegistry[task.id].success = true;
updateAllClients();
});
promiseRun.catch((error) => {
const taskEntry = this.taskRegistry[task.id];
if (!taskEntry) return;
this.taskRegistry[task.id].success = false;
this.taskRegistry[task.id].error = error;
updateAllClients();
});
});
}
connect(id: string, taskId: string, peer: PeerImpl) {
connect(id: string, taskId: string, peer: PeerImpl, isAdmin = false) {
const task = this.taskRegistry[taskId];
if (!task) return false;
if (task.requireAdmin && !isAdmin) return false;
this.clientRegistry[id] = peer;
this.taskRegistry[taskId].clients[id] = true; // Uniquely insert client to avoid sending duplicate traffic
const catchupMessage: TaskMessage = {
id: taskId,
name: task.name,
success: task.success,
error: task.error,
log: task.log,
@@ -127,10 +137,12 @@ export interface Task {
id: string;
name: string;
run: (context: TaskRunContext) => Promise<void>;
requireAdmin?: boolean;
}
export type TaskMessage = {
id: string;
name: string;
success: boolean;
progress: number;
error: undefined | string;