123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- <!DOCTYPE html>
- <html>
- <head>
- <!-- Notes: This should be open in its original path-->
- <meta charset="utf-8" />
- <link rel="stylesheet" href="../script/semantic/semantic.min.css" />
- <script src="../script/jquery-3.6.0.min.js"></script>
- <script src="../script/semantic/semantic.min.js"></script>
- </head>
- <body>
- <link rel="stylesheet" href="../darktheme.css">
- <script src="../script/darktheme.js"></script>
- <br />
- <div class="ui container">
- <div class="ui form">
- <div class="field">
- <input
- id="searchbar"
- type="text"
- placeholder="Search..."
- autocomplete="off"
- />
- </div>
- <div class="field">
- <div class="ui checkbox">
- <input type="checkbox" id="showUnexposed" class="hidden" />
- <label for="showUnexposed"
- >Show Containers with unexposed ports
- <br />
- <small
- >Please make sure Zoraxy and the target container share a
- network</small
- >
- </label>
- </div>
- </div>
- </div>
- <div class="ui header">
- <div class="content">
- List of Docker Containers
- <div class="sub header">
- Below is a list of all detected Docker containers currently running
- on the system.
- </div>
- </div>
- </div>
- <div id="containersList" class="ui middle aligned divided list active">
- <div class="ui loader active"></div>
- </div>
- <div class="ui horizontal divider"></div>
- <div id="containersAddedListHeader" class="ui header" hidden>
- Already added containers:
- </div>
- <div
- id="containersAddedList"
- class="ui middle aligned divided list"
- ></div>
- </div>
- <script>
- // debounce function to prevent excessive calls to a function
- function debounce(func, delay) {
- let timeout;
- return (...args) => {
- clearTimeout(timeout);
- timeout = setTimeout(() => func(...args), delay);
- };
- }
- // wait until DOM is fully loaded before executing script
- $(document).ready(() => {
- const $containersList = $("#containersList");
- const $containersAddedList = $("#containersAddedList");
- const $containersAddedListHeader = $("#containersAddedListHeader");
- const $searchbar = $("#searchbar");
- const $showUnexposed = $("#showUnexposed");
- let lines = {};
- let linesAdded = {};
- // load showUnexposed checkbox state from local storage
- function loadShowUnexposedState() {
- const storedState = localStorage.getItem("showUnexposed");
- if (storedState !== null) {
- $showUnexposed.prop("checked", storedState === "true");
- }
- }
- // save showUnexposed checkbox state to local storage
- function saveShowUnexposedState() {
- localStorage.setItem("showUnexposed", $showUnexposed.prop("checked"));
- }
- // fetch docker containers
- function getDockerContainers() {
- $containersList.html('<div class="ui loader active"></div>');
- $containersAddedList.empty();
- $containersAddedListHeader.attr("hidden", true);
- lines = {};
- linesAdded = {};
- const hostRequest = $.get("/api/proxy/list?type=host");
- const dockerRequest = $.get("/api/docker/containers");
- Promise.all([hostRequest, dockerRequest])
- .then(([hostData, dockerData]) => {
- if (!hostData.error && !dockerData.error) {
- processDockerData(hostData, dockerData);
- } else {
- showError(hostData.error || dockerData.error);
- }
- })
- .catch((error) => {
- console.error(error);
- parent.msgbox("Error loading data: " + error.message, false);
- });
- }
- // process docker data and update ui
- function processDockerData(hostData, dockerData) {
- const { containers } = dockerData;
- const existingTargets = new Set(
- hostData.flatMap(({ ActiveOrigins }) =>
- ActiveOrigins.map(({ OriginIpOrDomain }) => OriginIpOrDomain)
- )
- );
- containers.forEach((container) => {
- const name = container.Names[0].replace(/^\//, "");
- container.Ports.forEach((portObject) => {
- let port = portObject.PublicPort || portObject.PrivatePort;
- if (!portObject.PublicPort && !$showUnexposed.is(":checked"))
- return;
- // if port is not exposed, use container's name and let docker handle the routing
- // BUT this will only work if the container is on the same network as Zoraxy
- const targetAddress = portObject.IP || name;
- const key = `${name}-${port}`;
- if (
- existingTargets.has(`${targetAddress}:${port}`) &&
- !linesAdded[key]
- ) {
- linesAdded[key] = { name, ip: targetAddress, port };
- } else if (!lines[key]) {
- lines[key] = { name, ip: targetAddress, port };
- }
- });
- });
- // update ui
- updateContainersList();
- updateAddedContainersList();
- }
- // update containers list
- function updateContainersList() {
- $containersList.empty();
- Object.entries(lines).forEach(([key, line]) => {
- $containersList.append(`
- <div class="item">
- <div class="right floated content">
- <div class="ui button add-button" data-key="${key}">Add</div>
- </div>
- <div class="content">
- <div class="header">${line.name}</div>
- <div class="description">${line.ip}:${line.port}</div>
- </div>
- </div>
- `);
- });
- $containersList.find(".loader").removeClass("active");
- }
- // update the added containers list
- function updateAddedContainersList() {
- Object.entries(linesAdded).forEach(([key, line]) => {
- $containersAddedList.append(`
- <div class="item">
- <div class="content">
- <div class="header">${line.name}</div>
- <div class="description">${line.ip}:${line.port}</div>
- </div>
- </div>
- `);
- });
- if (Object.keys(linesAdded).length) {
- $containersAddedListHeader.removeAttr("hidden");
- }
- }
- // show error message
- function showError(error) {
- $containersList.html(
- `<div class="ui basic segment"><i class="ui red times icon"></i> ${error}</div>`
- );
- parent.msgbox(`Error loading data: ${error}`, false);
- }
- //
- // event listeners
- //
- $showUnexposed.on("change", () => {
- saveShowUnexposedState(); // save the new state to local storage
- getDockerContainers();
- });
- $searchbar.on(
- "input",
- debounce(() => {
- // debounce searchbar input with 300ms delay, then filter list
- // this prevents excessive calls to the filter function
- const search = $searchbar.val().toLowerCase();
- $("#containersList .item").each((index, item) => {
- const content = $(item).text().toLowerCase();
- $(item).toggle(content.includes(search));
- });
- }, 300)
- );
- $containersList.on("click", ".add-button", (event) => {
- const key = $(event.currentTarget).data("key");
- if (lines[key]) {
- parent.addContainerItem(lines[key]);
- }
- });
- //
- // initial calls
- //
- // load state of showUnexposed checkbox
- loadShowUnexposedState();
- // initial load of docker containers
- getDockerContainers();
- });
- </script>
- </body>
- </html>
|