|
134 | 134 | if (id) return node.id === id; |
135 | 135 | return !!node.is_self; |
136 | 136 | } |
| 137 | + // Resolve the node for a session descriptor. An EXPLICIT nodeId must |
| 138 | + // NEVER be silently downgraded to selfNode()/nodes[0]: if it isn't in |
| 139 | + // this window's (possibly stale or popup-local) node list, honour it as |
| 140 | + // a remote node by id so the socket routes to /ws/remote-console/<id> |
| 141 | + // and the right runtime runs on the right host. Without this, a 2nd tab |
| 142 | + // whose node wasn't resolvable fell back to the 1st tab's / self node |
| 143 | + // and ran the wrong command — pct enter on a native LXC, or lxc-attach |
| 144 | + // on a Proxmox LXC ("Failed to get init pid" / "vmid type check failed |
| 145 | + // - got 'gateway'"). A bare nodeId (none given) means the local session. |
| 146 | + function resolveNode(nodeId) { |
| 147 | + if (!nodeId) return selfNode() || null; |
| 148 | + const found = nodeById(nodeId); |
| 149 | + if (found) return found; |
| 150 | + const sid = selfNodeId(); |
| 151 | + if (sid && nodeId === sid) return selfNode() || null; |
| 152 | + return { id: nodeId }; // explicit remote node we can't (yet) resolve |
| 153 | + } |
137 | 154 | function wsBase() { |
138 | 155 | if (opts.wsBase) return opts.wsBase.replace(/\/+$/, ''); |
139 | 156 | const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; |
|
867 | 884 | // instead of spawning a duplicate). |
868 | 885 | function descriptorKey(o) { |
869 | 886 | o = o || {}; |
870 | | - const node = (o.nodeId && nodeById(o.nodeId)) || selfNode() || null; |
| 887 | + const node = resolveNode((o || {}).nodeId); |
871 | 888 | const nodeKey = (node && node.id) || 'local'; |
872 | 889 | const type = o.type || 'host'; |
873 | 890 | if (type === 'pve') return `${nodeKey}|pve|${o.pveNodeId}|${o.pveVmid}`; |
|
888 | 905 |
|
889 | 906 | function addTab(o) { |
890 | 907 | o = o || {}; |
891 | | - // null node = local serving node (see newWindow/wsUrlForPane). |
892 | | - const node = (o.nodeId && nodeById(o.nodeId)) || selfNode() || null; |
| 908 | + // Honour an explicit nodeId even if it isn't in this window's node |
| 909 | + // list (see resolveNode) — never fall back to the wrong host, which |
| 910 | + // ran pct/lxc-attach against the previous tab's node. |
| 911 | + const node = resolveNode(o.nodeId); |
893 | 912 | const type = o.type || 'host'; |
894 | 913 | const target = (type === 'host' || type === 'pve') ? '' : (o.name || o.target || ''); |
895 | 914 | const win = newWindow({ |
|
924 | 943 | // target — open it as tab 1 even when no node object resolves (a local |
925 | 944 | // host shell needs no node id; the node list is only for the picker). |
926 | 945 | const hasInitial = !!(initial.type || initial.name || initial.pveVmid); |
927 | | - const initialNode = (initial.nodeId && nodeById(initial.nodeId)) || selfNode() || null; |
| 946 | + const initialNode = resolveNode(initial.nodeId); |
928 | 947 | if (hasInitial) { |
929 | 948 | const type = initial.type || 'host'; |
930 | 949 | const target = (type === 'host' || type === 'pve') ? '' : (initial.name || initial.target || ''); |
|
0 commit comments