Canvas: Render network nodes

Change-Id: I63938da205af9377a1e210c0e972591142211a68
diff --git a/apps/canvas/front/src/components/node-gateway-https.tsx b/apps/canvas/front/src/components/node-gateway-https.tsx
index fdf710d..1397f5c 100644
--- a/apps/canvas/front/src/components/node-gateway-https.tsx
+++ b/apps/canvas/front/src/components/node-gateway-https.tsx
@@ -1,3 +1,4 @@
+import { v4 as uuidv4 } from "uuid";
 import { useStateStore, AppNode, GatewayHttpsNode, ServiceNode, nodeLabel, useEnv, nodeIsConnectable } from '@/lib/state';
 import { Handle, Position, useNodes } from '@xyflow/react';
 import { NodeRect } from './node-rect';
@@ -21,11 +22,20 @@
 
 export function NodeGatewayHttps(node: GatewayHttpsNode) {
   const { id, selected } = node;
+  const isConnectableNetwork = useMemo(() => nodeIsConnectable(node, "subdomain"), [node]);
   const isConnectable = useMemo(() => nodeIsConnectable(node, "https"), [node]);
   return (
     <NodeRect id={id} selected={selected} type={node.type} state={node.data.state}>
       {nodeLabel(node)}
       <Handle
+        type={"source"}
+        id="subdomain" 
+        position={Position.Top}
+        isConnectable={isConnectableNetwork}
+        isConnectableStart={isConnectableNetwork} 
+        isConnectableEnd={isConnectableNetwork} 
+      />
+      <Handle
         type={"target"}
         id="https" 
         position={Position.Bottom}
@@ -51,17 +61,34 @@
   useEffect(() => {
     const sub = form.watch((value: DeepPartial<z.infer<typeof schema>>, { name }: { name?: keyof z.infer<typeof schema> | undefined, type?: EventType | undefined }) => {
       if (name === "network") {
-        if (value.network !== undefined) {
-          store.updateNodeData<"gateway-https">(id, { network: value.network });
-        } else {
-          
+        let edges = store.edges;
+        if (data.network !== undefined) {
+          edges = edges.filter((e) => {
+            console.log(e);
+            if (e.source === id && e.sourceHandle === "subdomain" && e.target === data.network && e.targetHandle === "subdomain") {
+              return false;
+            } else {
+              return true;
+            }
+          });
         }
+        if (value.network !== undefined) {
+          edges = edges.concat({
+            id: uuidv4(),
+            source: id,
+            sourceHandle: "subdomain",
+            target: value.network,
+            targetHandle: "subdomain",
+          });
+        }
+        store.setEdges(edges);
+        store.updateNodeData<"gateway-https">(id, { network: value.network });
       } else if (name === "subdomain") {
         store.updateNodeData<"gateway-https">(id, { subdomain: value.subdomain });
       }
     });
     return () => sub.unsubscribe();
-  }, [form, store]);
+  }, [id, data, form, store]);
   const connectedToForm = useForm<z.infer<typeof connectedToSchema>>({
     resolver: zodResolver(connectedToSchema),
     mode: "onChange",