Build sdxpinn-quecmanger ipk
-Removed no longer needed files -Created ipk for quecmanager -Thank you @dr-dolomite for your hard work on this! Co-Authored-By: Russel Yasol <73575327+dr-dolomite@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const form = document.getElementById("commandForm");
|
||||
const output = document.getElementById("output");
|
||||
const commandInput = document.getElementById("command");
|
||||
const sendButton = document.getElementById("sendButton");
|
||||
const commandHistory = document.getElementById("commandHistory");
|
||||
const noHistory = document.getElementById("noHistory");
|
||||
const clearHistoryButton = document.getElementById("clearHistory");
|
||||
const cooldownTimer = document.getElementById("cooldownTimer");
|
||||
|
||||
const COOLDOWN_DURATION = 1000; // 1 second cooldown
|
||||
let isLoading = false;
|
||||
let cooldownActive = false;
|
||||
|
||||
function setLoading(loading) {
|
||||
isLoading = loading;
|
||||
sendButton.classList.toggle("is-loading", loading);
|
||||
form.classList.toggle("loading", loading);
|
||||
}
|
||||
|
||||
function setCooldown() {
|
||||
cooldownActive = true;
|
||||
sendButton.classList.add("cooldown");
|
||||
let timeLeft = COOLDOWN_DURATION;
|
||||
|
||||
function updateTimer() {
|
||||
timeLeft -= 100;
|
||||
if (timeLeft <= 0) {
|
||||
cooldownActive = false;
|
||||
sendButton.classList.remove("cooldown");
|
||||
cooldownTimer.textContent = "";
|
||||
return;
|
||||
}
|
||||
cooldownTimer.textContent = `${(timeLeft / 1000).toFixed(1)}s`;
|
||||
setTimeout(updateTimer, 100);
|
||||
}
|
||||
|
||||
updateTimer();
|
||||
}
|
||||
|
||||
function updateHistoryVisibility() {
|
||||
const hasHistoryItems =
|
||||
commandHistory.querySelectorAll(".history-item").length > 0;
|
||||
noHistory.style.display = hasHistoryItems ? "none" : "block";
|
||||
clearHistoryButton.style.display = hasHistoryItems ? "block" : "none";
|
||||
}
|
||||
|
||||
function addToHistory(command, response) {
|
||||
const historyItem = document.createElement("div");
|
||||
historyItem.className = "box mb-2 history-item";
|
||||
historyItem.innerHTML = `
|
||||
<button class="delete delete-history" aria-label="delete"></button>
|
||||
<strong class="command-text">${command}</strong>
|
||||
<pre style="margin-top: 0.5rem; font-size: 0.85em; white-space: pre-wrap;">${response}</pre>
|
||||
`;
|
||||
|
||||
historyItem
|
||||
.querySelector(".command-text")
|
||||
.addEventListener("click", () => {
|
||||
commandInput.value = command;
|
||||
commandInput.focus();
|
||||
});
|
||||
|
||||
historyItem
|
||||
.querySelector(".delete-history")
|
||||
.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
historyItem.classList.add("fade-out");
|
||||
setTimeout(() => {
|
||||
historyItem.remove();
|
||||
updateHistoryVisibility();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
commandHistory.insertBefore(historyItem, commandHistory.firstChild);
|
||||
updateHistoryVisibility();
|
||||
}
|
||||
|
||||
clearHistoryButton.addEventListener("click", () => {
|
||||
const historyItems = commandHistory.querySelectorAll(".history-item");
|
||||
historyItems.forEach((item) => {
|
||||
item.classList.add("fade-out");
|
||||
});
|
||||
setTimeout(() => {
|
||||
commandHistory.innerHTML = "";
|
||||
commandHistory.appendChild(noHistory);
|
||||
updateHistoryVisibility();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
async function sendCommand(command) {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch("/cgi-bin/atinout_handler.sh", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: `command=${encodeURIComponent(command)}`,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
output.value = data.output || "No response received";
|
||||
addToHistory(command, data.output || "No response received");
|
||||
setCooldown();
|
||||
} catch (error) {
|
||||
const errorMessage = `Error: ${error.message}\n\nTroubleshooting steps:\n1. Check if the device is connected\n2. Verify AT port settings\n3. Ensure atinout utility is installed`;
|
||||
output.value = errorMessage;
|
||||
addToHistory(command, errorMessage);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
form.addEventListener("submit", async function (e) {
|
||||
e.preventDefault();
|
||||
if (isLoading || cooldownActive) return;
|
||||
|
||||
const command = commandInput.value.trim();
|
||||
if (!command) {
|
||||
output.value = "Please enter a command";
|
||||
return;
|
||||
}
|
||||
|
||||
await sendCommand(command);
|
||||
commandInput.value = "";
|
||||
});
|
||||
|
||||
// Initialize visibility
|
||||
updateHistoryVisibility();
|
||||
});
|
||||
@@ -0,0 +1,377 @@
|
||||
// API Module - Handles all server communications
|
||||
const api = {
|
||||
async fetchCurrentSettings() {
|
||||
try {
|
||||
const response = await fetch("/cgi-bin/advance/advanced_settings.sh");
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("Current settings:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching settings:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async fetchConnectedDevices() {
|
||||
try {
|
||||
const response = await fetch("/cgi-bin/advance/fetch_macs.sh");
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching devices:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async sendATCommand(command) {
|
||||
try {
|
||||
const response = await fetch("/cgi-bin/atinout_handler.sh", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: "command=" + encodeURIComponent(command)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("AT command response:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error sending AT command:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// UI Manager Module - Handles all DOM interactions and UI updates
|
||||
const uiManager = {
|
||||
elements: {
|
||||
ipPassthrough: () => document.getElementById("ip-passthrough-mode"),
|
||||
dnsProxy: () => document.getElementById("dns-proxy-mode"),
|
||||
usbModem: () => document.getElementById("usb-modem-protocol"),
|
||||
connectedDevices: () => document.getElementById("connected-devices"),
|
||||
loadingContent: () => document.getElementById("loading-content"),
|
||||
modalButtons: () => document.getElementById("modal-buttons"),
|
||||
countdown: () => document.getElementById("countdown"),
|
||||
rebootModal: () => document.getElementById("reboot-modal"),
|
||||
advancedSettingsIcons: () => document.querySelectorAll(".advanced-settings i")
|
||||
},
|
||||
|
||||
showLoadingSpinners() {
|
||||
this.elements.advancedSettingsIcons().forEach(icon => {
|
||||
icon.classList.add("fa-spinner", "fa-spin");
|
||||
});
|
||||
},
|
||||
|
||||
hideLoadingSpinners() {
|
||||
this.elements.advancedSettingsIcons().forEach(icon => {
|
||||
icon.classList.remove("fa-spinner", "fa-spin");
|
||||
});
|
||||
},
|
||||
|
||||
updatePassthroughModeState(isEnabled) {
|
||||
const select = this.elements.ipPassthrough();
|
||||
if (!select) return;
|
||||
|
||||
const helpText = select.parentElement.querySelector(".help");
|
||||
|
||||
if (isEnabled) {
|
||||
select.removeAttribute("disabled");
|
||||
select.classList.remove("is-warning");
|
||||
if (helpText) {
|
||||
helpText.textContent = "Select a passthrough mode to apply.";
|
||||
helpText.classList.remove("is-warning");
|
||||
helpText.classList.add("is-info");
|
||||
}
|
||||
} else {
|
||||
select.setAttribute("disabled", "disabled");
|
||||
select.classList.add("is-warning");
|
||||
select.value = "Select IP Passthrough Mode";
|
||||
if (helpText) {
|
||||
helpText.textContent = "Please select a device first.";
|
||||
helpText.classList.remove("is-info");
|
||||
helpText.classList.add("is-warning");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
populateConnectedDevices(devices) {
|
||||
const select = this.elements.connectedDevices();
|
||||
if (!select) {
|
||||
console.error("Connected devices select element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing options except the first one
|
||||
while (select.options.length > 1) {
|
||||
select.remove(1);
|
||||
}
|
||||
|
||||
// Add new options
|
||||
devices.forEach(device => {
|
||||
const option = document.createElement("option");
|
||||
option.value = device.mac;
|
||||
option.textContent = `${device.hostname} - ${device.mac}`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
},
|
||||
|
||||
showModal() {
|
||||
const modal = this.elements.rebootModal();
|
||||
if (modal) {
|
||||
modal.classList.add("is-active");
|
||||
}
|
||||
},
|
||||
|
||||
showLoadingContent() {
|
||||
this.elements.loadingContent().style.display = "flex";
|
||||
this.elements.modalButtons().style.display = "none";
|
||||
this.showModal();
|
||||
},
|
||||
|
||||
startCountdown(duration) {
|
||||
const countdownElement = this.elements.countdown();
|
||||
let countdown = duration;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
countdown--;
|
||||
countdownElement.textContent = countdown;
|
||||
|
||||
if (countdown <= 0) {
|
||||
clearInterval(interval);
|
||||
location.reload();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
setElementLoading(element, isLoading) {
|
||||
if (isLoading) {
|
||||
element.disabled = true;
|
||||
element.classList.add('is-loading');
|
||||
} else {
|
||||
element.disabled = false;
|
||||
element.classList.remove('is-loading');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Settings Manager Module - Handles settings logic and updates
|
||||
const settingsManager = {
|
||||
async updateSettings(data) {
|
||||
const elements = {
|
||||
ipPassthrough: uiManager.elements.ipPassthrough(),
|
||||
dnsProxy: uiManager.elements.dnsProxy(),
|
||||
usbModem: uiManager.elements.usbModem()
|
||||
};
|
||||
|
||||
// Validate required elements
|
||||
const missingElements = Object.entries(elements)
|
||||
.filter(([, element]) => !element)
|
||||
.map(([key]) => key);
|
||||
|
||||
if (missingElements.length > 0) {
|
||||
console.error("Missing DOM elements:", missingElements);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
uiManager.updatePassthroughModeState(false);
|
||||
|
||||
// Update IP Passthrough Mode
|
||||
const mpdnRuleLine = data[0].response.split("\n")[1];
|
||||
if (mpdnRuleLine) {
|
||||
const mpdnRule = mpdnRuleLine.split(":")[1].trim();
|
||||
const passthroughMode = this.getPassthroughModeValue(mpdnRule);
|
||||
elements.ipPassthrough.value = passthroughMode;
|
||||
elements.ipPassthrough.setAttribute("data-current-mode", passthroughMode);
|
||||
}
|
||||
|
||||
// Update DNS Proxy
|
||||
const dnsProxyLine = data[1].response.split("\n")[1].split(":")[1].split(",")[1].trim();
|
||||
const dnsProxyMode = dnsProxyLine === '"disable"' ? "Disabled" : "Enabled";
|
||||
elements.dnsProxy.value = dnsProxyMode;
|
||||
elements.dnsProxy.setAttribute("data-current-mode", dnsProxyMode);
|
||||
|
||||
// Update USB Modem Protocol
|
||||
const usbModemProtocolLine = data[2].response.split("\n")[1].split(":")[1].split(",")[1].trim();
|
||||
const usbModemMode = this.getUsbModemProtocolValue(usbModemProtocolLine);
|
||||
elements.usbModem.value = usbModemMode;
|
||||
elements.usbModem.setAttribute("data-current-protocol", usbModemMode);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error updating settings:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
getPassthroughModeValue(mpdnRule) {
|
||||
const modes = {
|
||||
'"MPDN_rule",0,0,0,0,0': "Disabled",
|
||||
'"MPDN_rule",0,1,0,1,1': "ETH Only",
|
||||
'"MPDN_rule",0,1,0,3,1': "USB Only"
|
||||
};
|
||||
return modes[mpdnRule] || "Select IP Passthrough Mode";
|
||||
},
|
||||
|
||||
getUsbModemProtocolValue(protocol) {
|
||||
const protocols = {
|
||||
"0": "RMNET",
|
||||
"1": "ECM (Recommended)",
|
||||
"2": "MBIM",
|
||||
"3": "RNDIS"
|
||||
};
|
||||
return protocols[protocol] || "Select USB Modem Protocol";
|
||||
}
|
||||
};
|
||||
|
||||
// Event Handlers Module - Handles all event listeners
|
||||
const eventHandlers = {
|
||||
async handleDnsProxyChange(e) {
|
||||
const element = e.target;
|
||||
const selectedMode = element.value;
|
||||
const currentMode = element.getAttribute("data-current-mode");
|
||||
|
||||
if (selectedMode !== currentMode) {
|
||||
const command = selectedMode === "Enabled"
|
||||
? 'AT+QMAP="DHCPV4DNS","enable"'
|
||||
: 'AT+QMAP="DHCPV4DNS","disable"';
|
||||
|
||||
uiManager.setElementLoading(element, true);
|
||||
|
||||
try {
|
||||
const response = await api.sendATCommand(command);
|
||||
if (response.output.includes("OK")) {
|
||||
element.setAttribute("data-current-mode", selectedMode);
|
||||
// uiManager.showSuccessMessage("DNS Proxy setting updated successfully");
|
||||
} else {
|
||||
element.value = currentMode;
|
||||
// uiManager.showErrorMessage("Failed to update DNS Proxy setting");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending AT command:", error);
|
||||
element.value = currentMode;
|
||||
// uiManager.showErrorMessage("Error updating DNS Proxy setting");
|
||||
} finally {
|
||||
// uiManager.setElementLoading(element, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async handleIpPassthroughChange(e) {
|
||||
const element = e.target;
|
||||
const selectedMode = element.value;
|
||||
const currentMode = element.getAttribute("data-current-mode");
|
||||
const selectedDeviceMAC = uiManager.elements.connectedDevices().value;
|
||||
|
||||
if (selectedMode !== currentMode) {
|
||||
const commands = {
|
||||
"Disabled": 'AT+QMAP="MPDN_rule",0;+QPOWD=1',
|
||||
"ETH Only": `AT+QMAP="MPDN_rule",0,1,0,1,1,"${selectedDeviceMAC}";+QPOWD=1`,
|
||||
"USB Only": `AT+QMAP="MPDN_rule",0,1,0,3,1,"${selectedDeviceMAC}";+QPOWD=1`
|
||||
};
|
||||
|
||||
const command = commands[selectedMode];
|
||||
if (command) {
|
||||
uiManager.showLoadingContent();
|
||||
uiManager.startCountdown(90);
|
||||
try {
|
||||
await api.sendATCommand(command);
|
||||
} catch (error) {
|
||||
uiManager.showErrorMessage("Error updating IP Passthrough mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async handleUsbModemProtocolChange(e) {
|
||||
const element = e.target;
|
||||
const selectedProtocol = element.value;
|
||||
const currentProtocol = element.getAttribute("data-current-protocol");
|
||||
|
||||
if (selectedProtocol !== currentProtocol) {
|
||||
const commands = {
|
||||
"RMNET": 'AT+QCFG="usbnet",0;+CFUN=1,1',
|
||||
"ECM (Recommended)": 'AT+QCFG="usbnet",1;+CFUN=1,1',
|
||||
"MBIM": 'AT+QCFG="usbnet",2;+CFUN=1,1',
|
||||
"RNDIS": 'AT+QCFG="usbnet",3;+CFUN=1,1'
|
||||
};
|
||||
|
||||
const command = commands[selectedProtocol];
|
||||
if (command) {
|
||||
uiManager.showLoadingContent();
|
||||
uiManager.startCountdown(90);
|
||||
try {
|
||||
await api.sendATCommand(command);
|
||||
} catch (error) {
|
||||
uiManager.showErrorMessage("Error updating USB Modem Protocol");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleDeviceSelection(e) {
|
||||
const selectedMAC = e.target.value;
|
||||
const selectedHostname = e.target.options[e.target.selectedIndex].text;
|
||||
console.log("Selected device:", { mac: selectedMAC, hostname: selectedHostname });
|
||||
|
||||
const isDeviceSelected = selectedMAC !== "Select Device MAC";
|
||||
uiManager.updatePassthroughModeState(isDeviceSelected);
|
||||
}
|
||||
};
|
||||
|
||||
// Application Initialization
|
||||
async function init() {
|
||||
uiManager.showLoadingSpinners();
|
||||
|
||||
try {
|
||||
const [settings, devices] = await Promise.all([
|
||||
api.fetchCurrentSettings(),
|
||||
api.fetchConnectedDevices()
|
||||
]);
|
||||
|
||||
if (settings) {
|
||||
const updateSuccess = await settingsManager.updateSettings(settings);
|
||||
if (updateSuccess) {
|
||||
// Set up event listeners
|
||||
const dnsProxyElement = uiManager.elements.dnsProxy();
|
||||
const ipPassthroughElement = uiManager.elements.ipPassthrough();
|
||||
const usbModemElement = uiManager.elements.usbModem();
|
||||
const connectedDevicesElement = uiManager.elements.connectedDevices();
|
||||
|
||||
if (dnsProxyElement) {
|
||||
dnsProxyElement.addEventListener("change", eventHandlers.handleDnsProxyChange);
|
||||
}
|
||||
if (ipPassthroughElement) {
|
||||
ipPassthroughElement.addEventListener("change", eventHandlers.handleIpPassthroughChange);
|
||||
}
|
||||
if (usbModemElement) {
|
||||
usbModemElement.addEventListener("change", eventHandlers.handleUsbModemProtocolChange);
|
||||
}
|
||||
if (connectedDevicesElement) {
|
||||
connectedDevicesElement.addEventListener("change", eventHandlers.handleDeviceSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (devices) {
|
||||
uiManager.populateConnectedDevices(devices);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Initialization error:", error);
|
||||
uiManager.showErrorMessage("Error initializing settings");
|
||||
} finally {
|
||||
uiManager.hideLoadingSpinners();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
@@ -0,0 +1,299 @@
|
||||
// api.js - API related functions
|
||||
const api = {
|
||||
async fetch(endpoint, options = {}) {
|
||||
try {
|
||||
const response = await fetch(endpoint, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(`API Error (${endpoint}):`, error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async fetchCurrentSettings() {
|
||||
const data = await this.fetch("/cgi-bin/advanced_settings.sh");
|
||||
console.log("Current settings:", data);
|
||||
return data;
|
||||
},
|
||||
|
||||
async fetchConnectedDevices() {
|
||||
const data = await this.fetch("/cgi-bin/fetch_macs.sh");
|
||||
return data;
|
||||
},
|
||||
|
||||
async sendATCommand(command) {
|
||||
return await this.fetch("/cgi-bin/atinout_handler.sh", {
|
||||
method: "POST",
|
||||
body: "command=" + encodeURIComponent(command)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// uiManager.js - UI related functions
|
||||
const uiManager = {
|
||||
elements: {
|
||||
ipPassthrough: () => document.getElementById("ip-passthrough-mode"),
|
||||
dnsProxy: () => document.getElementById("dns-proxy-mode"),
|
||||
usbModem: () => document.getElementById("usb-modem-protocol"),
|
||||
connectedDevices: () => document.getElementById("connected-devices"),
|
||||
loadingContent: () => document.getElementById("loading-content"),
|
||||
modalButtons: () => document.getElementById("modal-buttons"),
|
||||
countdown: () => document.getElementById("countdown"),
|
||||
rebootModal: () => document.getElementById("reboot-modal"),
|
||||
advancedSettingsIcons: () => document.querySelectorAll(".advanced-settings i")
|
||||
},
|
||||
|
||||
showLoadingSpinners() {
|
||||
this.elements.advancedSettingsIcons().forEach(icon => {
|
||||
icon.classList.add("fa-spinner", "fa-spin");
|
||||
});
|
||||
},
|
||||
|
||||
hideLoadingSpinners() {
|
||||
this.elements.advancedSettingsIcons().forEach(icon => {
|
||||
icon.classList.remove("fa-spinner", "fa-spin");
|
||||
});
|
||||
},
|
||||
|
||||
updatePassthroughModeState(isEnabled) {
|
||||
const select = this.elements.ipPassthrough();
|
||||
if (!select) return;
|
||||
|
||||
const helpText = select.parentElement.querySelector(".help");
|
||||
|
||||
if (isEnabled) {
|
||||
select.removeAttribute("disabled");
|
||||
select.classList.remove("is-warning");
|
||||
if (helpText) {
|
||||
helpText.textContent = "Select a passthrough mode to apply.";
|
||||
helpText.classList.remove("is-warning");
|
||||
helpText.classList.add("is-info");
|
||||
}
|
||||
} else {
|
||||
select.setAttribute("disabled", "disabled");
|
||||
select.classList.add("is-warning");
|
||||
select.value = "Select IP Passthrough Mode";
|
||||
if (helpText) {
|
||||
helpText.textContent = "Please select a device first.";
|
||||
helpText.classList.remove("is-info");
|
||||
helpText.classList.add("is-warning");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
populateConnectedDevices(devices) {
|
||||
const select = this.elements.connectedDevices();
|
||||
if (!select) {
|
||||
console.error("Connected devices select element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing options except the first one
|
||||
while (select.options.length > 1) {
|
||||
select.remove(1);
|
||||
}
|
||||
|
||||
// Add new options
|
||||
devices.forEach(device => {
|
||||
const option = document.createElement("option");
|
||||
option.value = device.mac;
|
||||
option.textContent = `${device.hostname} - ${device.mac}`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
},
|
||||
|
||||
showModal() {
|
||||
const modal = this.elements.rebootModal();
|
||||
if (modal) {
|
||||
modal.classList.add("is-active");
|
||||
}
|
||||
},
|
||||
|
||||
showLoadingContent() {
|
||||
this.elements.loadingContent().style.display = "flex";
|
||||
this.elements.modalButtons().style.display = "none";
|
||||
this.showModal();
|
||||
},
|
||||
|
||||
startCountdown(duration) {
|
||||
const countdownElement = this.elements.countdown();
|
||||
let countdown = duration;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
countdown--;
|
||||
countdownElement.textContent = countdown;
|
||||
|
||||
if (countdown <= 0) {
|
||||
clearInterval(interval);
|
||||
location.reload();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// settingsManager.js - Settings management
|
||||
const settingsManager = {
|
||||
async updateSettings(data) {
|
||||
const elements = {
|
||||
ipPassthrough: uiManager.elements.ipPassthrough(),
|
||||
dnsProxy: uiManager.elements.dnsProxy(),
|
||||
usbModem: uiManager.elements.usbModem()
|
||||
};
|
||||
|
||||
// Validate required elements
|
||||
const missingElements = Object.entries(elements)
|
||||
.filter(([, element]) => !element)
|
||||
.map(([key]) => key);
|
||||
|
||||
if (missingElements.length > 0) {
|
||||
console.error("Missing DOM elements:", missingElements);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
uiManager.updatePassthroughModeState(false);
|
||||
|
||||
// Update IP Passthrough Mode
|
||||
const mpdnRuleLine = data[0].response.split("\n")[1];
|
||||
if (mpdnRuleLine) {
|
||||
const mpdnRule = mpdnRuleLine.split(":")[1].trim();
|
||||
elements.ipPassthrough.value = this.getPassthroughModeValue(mpdnRule);
|
||||
}
|
||||
|
||||
// Update DNS Proxy
|
||||
const dnsProxyLine = data[1].response.split("\n")[1].split(":")[1].split(",")[1].trim();
|
||||
elements.dnsProxy.value = dnsProxyLine === '"disable"' ? "Disabled" : "Enabled";
|
||||
|
||||
// Update USB Modem Protocol
|
||||
const usbModemProtocolLine = data[2].response.split("\n")[1].split(":")[1].split(",")[1].trim();
|
||||
elements.usbModem.value = this.getUsbModemProtocolValue(usbModemProtocolLine);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error updating settings:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
getPassthroughModeValue(mpdnRule) {
|
||||
const modes = {
|
||||
'"MPDN_rule",0,0,0,0,0': "Disabled",
|
||||
'"MPDN_rule",0,1,0,1,1': "ETH Only",
|
||||
'"MPDN_rule",0,1,0,3,1': "USB Only"
|
||||
};
|
||||
return modes[mpdnRule] || "Select IP Passthrough Mode";
|
||||
},
|
||||
|
||||
getUsbModemProtocolValue(protocol) {
|
||||
const protocols = {
|
||||
"0": "RMNET",
|
||||
"1": "ECM (Recommended)",
|
||||
"2": "MBIM",
|
||||
"3": "RNDIS"
|
||||
};
|
||||
return protocols[protocol] || "Select USB Modem Protocol";
|
||||
}
|
||||
};
|
||||
|
||||
// eventHandlers.js - Event handling
|
||||
const eventHandlers = {
|
||||
async handleDnsProxyChange(e) {
|
||||
const selectedMode = e.target.value;
|
||||
const currentMode = e.target.getAttribute("data-current-mode");
|
||||
|
||||
if (selectedMode !== currentMode) {
|
||||
const command = selectedMode === "Enabled"
|
||||
? 'AT+QMAP="DHCPV4DNS","enable"'
|
||||
: 'AT+QMAP="DHCPV4DNS","disable"';
|
||||
await api.sendATCommand(command);
|
||||
}
|
||||
},
|
||||
|
||||
async handleIpPassthroughChange(e) {
|
||||
const selectedMode = e.target.value;
|
||||
const currentMode = e.target.getAttribute("data-current-mode");
|
||||
const selectedDeviceMAC = uiManager.elements.connectedDevices().value;
|
||||
|
||||
if (selectedMode !== currentMode) {
|
||||
const commands = {
|
||||
"Disabled": 'AT+QMPDN="MPDN_rule",0;+CFUN=1,1',
|
||||
"ETH Only": `AT+QMPDN="MPDN_rule",0,1,0,1,1,"${selectedDeviceMAC}"`,
|
||||
"USB Only": `AT+QMPDN="MPDN_rule",0,1,0,3,1,"${selectedDeviceMAC}"`
|
||||
};
|
||||
|
||||
const command = commands[selectedMode];
|
||||
if (command) {
|
||||
uiManager.showLoadingContent();
|
||||
uiManager.startCountdown(80);
|
||||
await api.sendATCommand(command);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async handleUsbModemProtocolChange(e) {
|
||||
const selectedProtocol = e.target.value;
|
||||
const currentProtocol = e.target.getAttribute("data-current-protocol");
|
||||
|
||||
if (selectedProtocol !== currentProtocol) {
|
||||
const commands = {
|
||||
"RMNET": 'AT+QCFG="usbnet",0;+CFUN=1,1',
|
||||
"ECM (Recommended)": 'AT+QCFG="usbnet",1;+CFUN=1,1',
|
||||
"MBIM": 'AT+QCFG="usbnet",2;+CFUN=1,1',
|
||||
"RNDIS": 'AT+QCFG="usbnet",3;+CFUN=1,1'
|
||||
};
|
||||
|
||||
const command = commands[selectedProtocol];
|
||||
if (command) {
|
||||
uiManager.showLoadingContent();
|
||||
uiManager.startCountdown(80);
|
||||
await api.sendATCommand(command);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleDeviceSelection(e) {
|
||||
const selectedMAC = e.target.value;
|
||||
const selectedHostname = e.target.options[e.target.selectedIndex].text;
|
||||
console.log("Selected device:", { mac: selectedMAC, hostname: selectedHostname });
|
||||
|
||||
const isDeviceSelected = selectedMAC !== "Select Device MAC";
|
||||
uiManager.updatePassthroughModeState(isDeviceSelected);
|
||||
}
|
||||
};
|
||||
|
||||
// main.js - Application initialization
|
||||
async function init() {
|
||||
uiManager.showLoadingSpinners();
|
||||
|
||||
try {
|
||||
const [settings, devices] = await Promise.all([
|
||||
api.fetchCurrentSettings(),
|
||||
api.fetchConnectedDevices()
|
||||
]);
|
||||
|
||||
if (settings) {
|
||||
const updateSuccess = await settingsManager.updateSettings(settings);
|
||||
if (updateSuccess) {
|
||||
uiManager.hideLoadingSpinners();
|
||||
|
||||
// Set up event listeners
|
||||
uiManager.elements.dnsProxy().addEventListener("change", eventHandlers.handleDnsProxyChange);
|
||||
uiManager.elements.ipPassthrough().addEventListener("change", eventHandlers.handleIpPassthroughChange);
|
||||
uiManager.elements.usbModem().addEventListener("change", eventHandlers.handleUsbModemProtocolChange);
|
||||
uiManager.elements.connectedDevices().addEventListener("change", eventHandlers.handleDeviceSelection);
|
||||
}
|
||||
}
|
||||
|
||||
if (devices) {
|
||||
uiManager.populateConnectedDevices(devices);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Initialization error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
@@ -0,0 +1,94 @@
|
||||
// TTL Control functionality
|
||||
const TTLControl = {
|
||||
async getCurrentState() {
|
||||
try {
|
||||
const response = await fetch('/cgi-bin/advance/ttl.sh');
|
||||
const data = await response.json();
|
||||
return {
|
||||
isEnabled: data.isEnabled,
|
||||
currentValue: data.currentValue || 0
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching TTL state:', error);
|
||||
return { isEnabled: false, currentValue: 0 };
|
||||
}
|
||||
},
|
||||
|
||||
async setTTLValue(value) {
|
||||
try {
|
||||
const response = await fetch('/cgi-bin/advance/ttl.sh', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `ttl=${value}`
|
||||
});
|
||||
const result = await response.json();
|
||||
return result.success;
|
||||
} catch (error) {
|
||||
console.error('Error setting TTL value:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
updateUI(isEnabled, value) {
|
||||
const stateInput = document.getElementById('ttl-state');
|
||||
const valueInput = document.getElementById('ttl-current-value');
|
||||
const stateIcon = stateInput.nextElementSibling.querySelector('i');
|
||||
const valueIcon = valueInput.nextElementSibling.querySelector('i');
|
||||
|
||||
// Update State UI
|
||||
if (isEnabled) {
|
||||
// Enabled state
|
||||
stateInput.value = 'Enabled';
|
||||
stateInput.classList.remove('has-text-warning', 'is-danger');
|
||||
stateInput.classList.add('has-text-success', 'has-text-weight-bold');
|
||||
stateIcon.classList.remove('fa-exclamation-triangle', 'has-text-warning');
|
||||
stateIcon.classList.add('fa-check', 'has-text-success');
|
||||
} else {
|
||||
// Disabled state
|
||||
stateInput.value = 'Disabled';
|
||||
stateInput.classList.remove('has-text-success', 'is-danger');
|
||||
stateInput.classList.add('has-text-warning', 'has-text-weight-bold');
|
||||
stateIcon.classList.remove('fa-check', 'has-text-success');
|
||||
stateIcon.classList.add('fa-exclamation-triangle', 'has-text-warning');
|
||||
}
|
||||
|
||||
// Update Value UI
|
||||
valueInput.value = value.toString();
|
||||
valueInput.classList.add('has-text-weight-bold', 'has-text-white');
|
||||
if (isEnabled) {
|
||||
valueIcon.classList.remove('fa-exclamation-triangle', 'has-text-warning');
|
||||
valueIcon.classList.add('fa-check', 'has-text-success');
|
||||
} else {
|
||||
valueIcon.classList.remove('fa-check', 'has-text-success');
|
||||
valueIcon.classList.add('fa-exclamation-triangle', 'has-text-warning');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Event Listeners
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
// Initial state fetch
|
||||
const { isEnabled, currentValue } = await TTLControl.getCurrentState();
|
||||
TTLControl.updateUI(isEnabled, currentValue);
|
||||
|
||||
// Submit button event listener
|
||||
document.getElementById('ttl-submit').addEventListener('click', async function() {
|
||||
const newValue = document.getElementById('ttl-set-value').value;
|
||||
const numValue = parseInt(newValue);
|
||||
|
||||
if (isNaN(numValue) || numValue < 0) {
|
||||
alert('Please enter a valid TTL value (0 or positive number)');
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await TTLControl.setTTLValue(numValue);
|
||||
if (success) {
|
||||
TTLControl.updateUI(numValue !== 0, numValue);
|
||||
alert('TTL settings updated successfully');
|
||||
} else {
|
||||
alert('Failed to update TTL settings');
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user