Canvas: Implement worker to manager communication

Register workers on manager side.
Let user force reload service workers.

Change-Id: I2635a04167e7c853151d8a1f5c3511646181a063
diff --git a/apps/canvas/front/src/components/actions.tsx b/apps/canvas/front/src/components/actions.tsx
index eb89b8a..509b51c 100644
--- a/apps/canvas/front/src/components/actions.tsx
+++ b/apps/canvas/front/src/components/actions.tsx
@@ -25,6 +25,7 @@
 	const instance = useReactFlow();
 	const [ok, setOk] = useState(false);
 	const [loading, setLoading] = useState(false);
+	const [reloading, setReloading] = useState(false);
 	useEffect(() => {
 		setOk(!messages.some((m) => m.type === "FATAL"));
 	}, [messages, setOk]);
@@ -64,7 +65,7 @@
 		}
 		setLoading(true);
 		try {
-			const config = generateDodoConfig(nodes, env);
+			const config = generateDodoConfig(projectId, nodes, env);
 			if (config == null) {
 				throw new Error("MUST NOT REACH!");
 			}
@@ -163,21 +164,64 @@
 			});
 		}
 	}, [store, clear, projectId, toast]);
-	const [props, setProps] = useState({});
+	const reload = useCallback(async () => {
+		if (projectId == null) {
+			return;
+		}
+		setReloading(true);
+		try {
+			const resp = await fetch(`/api/project/${projectId}/reload`, {
+				method: "POST",
+				headers: {
+					"Content-Type": "application/json",
+				},
+			});
+			if (resp.ok) {
+				toast({
+					title: "Reload triggered successfully",
+				});
+			} else {
+				toast({
+					variant: "destructive",
+					title: "Reload failed",
+					description: await resp.text(),
+				});
+			}
+		} catch (e) {
+			console.log(e);
+			toast({
+				variant: "destructive",
+				title: "Reload failed",
+			});
+		} finally {
+			setReloading(false);
+		}
+	}, [projectId, toast]);
+	const [deployProps, setDeployProps] = useState({});
+	const [reloadProps, setReloadProps] = useState({});
 	useEffect(() => {
 		if (loading) {
-			setProps({ loading: true });
+			setDeployProps({ loading: true });
 		} else if (ok) {
-			setProps({ disabled: false });
+			setDeployProps({ disabled: false });
 		} else {
-			setProps({ disabled: true });
+			setDeployProps({ disabled: true });
 		}
-	}, [ok, loading, setProps]);
+
+		if (reloading) {
+			setReloadProps({ loading: true });
+		} else {
+			setReloadProps({ disabled: projectId === undefined });
+		}
+	}, [ok, loading, reloading, projectId]);
 	return (
 		<>
-			<Button onClick={deploy} {...props}>
+			<Button onClick={deploy} {...deployProps}>
 				Deploy
 			</Button>
+			<Button onClick={reload} {...reloadProps}>
+				Reload
+			</Button>
 			<Button onClick={save}>Save</Button>
 			<Button onClick={restoreSaved}>Restore</Button>
 			<Button onClick={clear} variant="destructive">