|
2 | 2 | <html>
|
3 | 3 | <head>
|
4 | 4 | <!-- Notes: This should be open in its original path-->
|
5 |
| - <meta charset="utf-8"> |
| 5 | + <meta charset="utf-8" /> |
6 | 6 | <link rel="stylesheet" href="../script/semantic/semantic.min.css" />
|
7 | 7 | <script src="../script/jquery-3.6.0.min.js"></script>
|
8 | 8 | <script src="../script/semantic/semantic.min.js"></script>
|
9 | 9 | </head>
|
10 | 10 | <body>
|
11 | 11 | <br />
|
12 | 12 | <div class="ui container">
|
| 13 | + <div class="field"> |
| 14 | + <div class="ui checkbox"> |
| 15 | + <input type="checkbox" id="showUnexposed" class="hidden" /> |
| 16 | + <label for="showUnexposed" |
| 17 | + >Show Containers with Unexposed Ports |
| 18 | + <br /> |
| 19 | + <small |
| 20 | + >Please make sure Zoraxy and the target container share a |
| 21 | + network</small |
| 22 | + > |
| 23 | + </label> |
| 24 | + </div> |
| 25 | + </div> |
13 | 26 | <div class="ui header">
|
14 | 27 | <div class="content">
|
15 | 28 | List of Docker Containers
|
|
33 | 46 | </div>
|
34 | 47 |
|
35 | 48 | <script>
|
36 |
| - const lines = {}; |
37 |
| - const linesAded = {}; |
| 49 | + let lines = {}; |
| 50 | + let linesAdded = {}; |
| 51 | + |
| 52 | + document |
| 53 | + .getElementById("showUnexposed") |
| 54 | + .addEventListener("change", () => { |
| 55 | + console.log("showUnexposed", $("#showUnexposed").is(":checked")); |
| 56 | + $("#containersList").html('<div class="ui loader active"></div>'); |
| 57 | + |
| 58 | + $("#containersAddedList").empty(); |
| 59 | + $("#containersAddedListHeader").attr("hidden", true); |
| 60 | + |
| 61 | + lines = {}; |
| 62 | + linesAdded = {}; |
| 63 | + |
| 64 | + getDockerContainers(); |
| 65 | + }); |
38 | 66 |
|
39 | 67 | function getDockerContainers() {
|
40 | 68 | const hostRequest = $.get("/api/proxy/list?type=host");
|
41 | 69 | const dockerRequest = $.get("/api/docker/containers");
|
42 | 70 |
|
43 |
| - // Wait for both requests to complete |
44 | 71 | Promise.all([hostRequest, dockerRequest])
|
45 | 72 | .then(([hostData, dockerData]) => {
|
46 |
| - if ( |
47 |
| - dockerData.error === undefined && |
48 |
| - hostData.error === undefined |
49 |
| - ) { |
| 73 | + if (!dockerData.error && !hostData.error) { |
50 | 74 | const { containers, network } = dockerData;
|
51 |
| - const bridge = network.find(({ Name }) => Name === "bridge"); |
52 |
| - const { |
53 |
| - IPAM: { |
54 |
| - Config: [{ Gateway: gateway }], |
55 |
| - }, |
56 |
| - } = bridge; |
57 |
| - const existedDomains = hostData.reduce((acc, { ActiveOrigins }) => { |
58 |
| - return acc.concat(ActiveOrigins.map(({ OriginIpOrDomain }) => OriginIpOrDomain)); |
59 |
| - }, []); |
| 75 | + |
| 76 | + const existingTargets = new Set( |
| 77 | + hostData.flatMap(({ ActiveOrigins }) => |
| 78 | + ActiveOrigins.map(({ OriginIpOrDomain }) => OriginIpOrDomain) |
| 79 | + ) |
| 80 | + ); |
60 | 81 |
|
61 | 82 | for (const container of containers) {
|
62 |
| - const { |
63 |
| - Ports, |
64 |
| - Names: [name], |
65 |
| - } = container; |
66 |
| - |
67 |
| - for (const portObject of Ports.filter( |
68 |
| - ({ IP: ip }) => ip === "::" || ip === '0.0.0.0' |
69 |
| - )) { |
70 |
| - const { IP: ip, PublicPort: port } = portObject; |
| 83 | + const Ports = container.Ports; |
| 84 | + const name = container.Names[0].replace(/^\//, ""); |
| 85 | + |
| 86 | + for (const portObject of Ports) { |
| 87 | + let port = portObject.PublicPort; |
| 88 | + if (!port) { |
| 89 | + if (!$("#showUnexposed").is(":checked")) { |
| 90 | + continue; |
| 91 | + } |
| 92 | + port = portObject.PrivatePort; |
| 93 | + } |
71 | 94 | const key = `${name}-${port}`;
|
72 | 95 |
|
| 96 | + // if port is not exposed, use container's name and let docker handle the routing |
| 97 | + // BUT this will only work if the container is on the same network as Zoraxy |
| 98 | + const targetAddress = portObject.IP || name; |
| 99 | + |
73 | 100 | if (
|
74 |
| - existedDomains.some((item) => item === `${gateway}:${port}`) && |
75 |
| - !linesAded[key] |
| 101 | + existingTargets.has(`${targetAddress}:${port}`) && |
| 102 | + !linesAdded[key] |
76 | 103 | ) {
|
77 |
| - linesAded[key] = { |
78 |
| - name: name.replace(/^\//, ""), |
79 |
| - ip: gateway, |
| 104 | + linesAdded[key] = { |
| 105 | + name, |
| 106 | + ip: targetAddress, |
80 | 107 | port,
|
81 | 108 | };
|
82 | 109 | } else if (!lines[key]) {
|
83 | 110 | lines[key] = {
|
84 |
| - name: name.replace(/^\//, ""), |
85 |
| - ip: gateway, |
| 111 | + name, |
| 112 | + ip: targetAddress, |
86 | 113 | port,
|
87 | 114 | };
|
88 | 115 | }
|
|
92 | 119 | for (const [key, line] of Object.entries(lines)) {
|
93 | 120 | $("#containersList").append(
|
94 | 121 | `<div class="item">
|
95 |
| - <div class="right floated content"> |
96 |
| - <div class="ui button" onclick="addContainerItem('${key}');">Add</div> |
97 |
| - </div> |
98 |
| - <div class="content"> |
99 |
| - <div class="header">${line.name}</div> |
100 |
| - <div class="description"> |
101 |
| - ${line.ip}:${line.port} |
102 |
| - </div> |
103 |
| - </div>` |
| 122 | + <div class="right floated content"> |
| 123 | + <div class="ui button" onclick="addContainerItem('${key}');">Add</div> |
| 124 | + </div> |
| 125 | + <div class="content"> |
| 126 | + <div class="header">${line.name}</div> |
| 127 | + <div class="description"> |
| 128 | + ${line.ip}:${line.port} |
| 129 | + </div> |
| 130 | + </div>` |
104 | 131 | );
|
105 | 132 | }
|
106 |
| - for (const [key, line] of Object.entries(linesAded)) { |
| 133 | + |
| 134 | + for (const [key, line] of Object.entries(linesAdded)) { |
107 | 135 | $("#containersAddedList").append(
|
108 | 136 | `<div class="item">
|
109 |
| - <div class="content"> |
110 |
| - <div class="header">${line.name}</div> |
111 |
| - <div class="description"> |
112 |
| - ${line.ip}:${line.port} |
113 |
| - </div> |
114 |
| - </div>` |
| 137 | + <div class="content"> |
| 138 | + <div class="header">${line.name}</div> |
| 139 | + <div class="description"> |
| 140 | + ${line.ip}:${line.port} |
| 141 | + </div> |
| 142 | + </div>` |
115 | 143 | );
|
116 | 144 | }
|
117 |
| - Object.entries(linesAded).length && |
| 145 | + |
| 146 | + Object.entries(linesAdded).length && |
118 | 147 | $("#containersAddedListHeader").removeAttr("hidden");
|
119 | 148 | $("#containersList .loader").removeClass("active");
|
120 | 149 | } else {
|
121 | 150 | parent.msgbox(
|
122 | 151 | `Error loading data: ${dockerData.error || hostData.error}`,
|
123 | 152 | false
|
124 | 153 | );
|
125 |
| - $("#containersList").html(`<div class="ui basic segment"><i class="ui red times icon"></i> ${dockerData.error || hostData.error}</div>`); |
| 154 | + $("#containersList").html( |
| 155 | + `<div class="ui basic segment"><i class="ui red times icon"></i> ${ |
| 156 | + dockerData.error || hostData.error |
| 157 | + }</div>` |
| 158 | + ); |
126 | 159 | }
|
127 | 160 | })
|
128 | 161 | .catch((error) => {
|
|
0 commit comments