diff --git a/Dockerfile b/Dockerfile index aa012bc..66f0825 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM python:3.11-slim-bookworm +FROM python:3.11-slim # Install Node.js RUN apt-get update && apt-get install -y nodejs npm \ @@ -16,7 +16,7 @@ COPY app/requirements.txt /app/ RUN pip install --no-cache-dir -r /app/requirements.txt # Install Node.js dependencies and build TailwindCSS -COPY package*.json /app/ +COPY app/package*.json /app/ RUN npm install COPY app/ /app/ diff --git a/app/functions/utils.py b/app/functions/utils.py index f2a3834..6cefd89 100644 --- a/app/functions/utils.py +++ b/app/functions/utils.py @@ -50,7 +50,7 @@ def establish_connection(device_config): logger.error(f"Failed to establish connection to {device_config['host']}: {e}") raise -def execute_command(device, command_config, target): +def execute_command(device, command_format, target, ip_version): device_config = { 'device_type': device['type'], 'host': device['host'], @@ -68,7 +68,8 @@ def execute_command(device, command_config, target): try: with establish_connection(device_config) as connection: # Format the command - command = str(command_config.get('format').format(target=target).strip()) + print(command_format) + command = str(command_format.format(ip_version=ip_version, target=target).strip()) # Execute the command output = connection.send_command( diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..9fcdd3b --- /dev/null +++ b/app/package.json @@ -0,0 +1,8 @@ +{ + "name": "photonglass", + "version": "1.0.0", + "description": "Looking Glass", + "dependencies": { + "tailwindcss": "^3.4.16" + } +} diff --git a/app/static/js/main.js b/app/static/js/main.js index 23f65f2..58d47cf 100644 --- a/app/static/js/main.js +++ b/app/static/js/main.js @@ -5,8 +5,8 @@ const app = Vue.createApp({ selectedDevice: '', selectedCommand: '', targetIp: '', + selectedIpVersion: 'IPv4', // Default to IPv4 isLoading: false, - isLoadingIp: false, commandResult: '', devices: window.initialData?.devices || [], commands: window.initialData?.commands || [], @@ -22,23 +22,40 @@ const app = Vue.createApp({ }, mounted() { this.updateThemeClass(); - // Make sure commands are loaded - if (window.initialData?.commands) { - console.log('Commands loaded:', this.commands); - } }, watch: { selectedCommand: { handler(newVal) { this.currentCommand = this.commands.find(cmd => cmd.id === newVal); - console.log("Current command updated:", this.currentCommand); }, immediate: true } }, computed: { + showIpVersionSelector() { + if (!this.targetIp) return true; + + // Check if the input is not a valid IP address + const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/; + const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){0,7}:([0-9a-fA-F]{1,4}:){0,7}[0-9a-fA-F]{1,4}$|^::1$|^::$|^::ffff:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; + + // Detect IP version + if (ipv6Regex.test(this.targetIp)) { + this.selectedIpVersion = 'IPv6'; + return false; // Disable button for valid IPv6 + } else if (ipv4Regex.test(this.targetIp)) { + this.selectedIpVersion = 'IPv4'; + return false; // Disable button for valid IPv4 + } + + return true; // Enable button for non-IP input + }, isValidInput() { if (!this.currentCommand || !this.targetIp) return false; + + // If IP version selector is shown, any input is valid as it will be resolved + if (this.showIpVersionSelector) return true; + if (this.currentCommand.field?.validation) { const pattern = new RegExp(this.currentCommand.field.validation); return pattern.test(this.targetIp); @@ -47,22 +64,8 @@ const app = Vue.createApp({ } }, methods: { - async getMyIp() { - if (this.isLoadingIp) return; - this.isLoadingIp = true; - try { - const response = await fetch('/my-ip'); - const data = await response.json(); - if (response.ok && data.ip) { - this.targetIp = data.ip; - } else { - console.error('Failed to get IP address'); - } - } catch (error) { - console.error('Error fetching IP:', error); - } finally { - this.isLoadingIp = false; - } + toggleIpVersion() { + this.selectedIpVersion = this.selectedIpVersion === 'IPv4' ? 'IPv6' : 'IPv4'; }, toggleTheme() { this.isDark = !this.isDark; @@ -80,7 +83,7 @@ const app = Vue.createApp({ // Update the selected device state const wasDeselected = this.selectedDevice === device; this.selectedDevice = wasDeselected ? '' : device; - + // Only reset states if we're deselecting the device entirely if (wasDeselected) { this.selectedCommand = ''; @@ -111,12 +114,13 @@ const app = Vue.createApp({ body: JSON.stringify({ device: this.selectedDevice, command: this.selectedCommand, - target: this.targetIp + target: this.targetIp, + ipVersion: this.selectedIpVersion }) }); const data = await response.json(); - + if (!response.ok) { this.commandResult = `❌ Error: ${data.message || 'An unknown error occurred'}`; return; @@ -125,10 +129,10 @@ const app = Vue.createApp({ if (data.error) { // Handle error responses with specific error types const errorPrefix = data.error_type === 'timeout' ? '🕒 Timeout Error: ' : - data.error_type === 'auth' ? '🔒 Authentication Error: ' : - data.error_type === 'connection' ? '🔌 Connection Error: ' : - data.error_type === 'no_output' ? '📭 No Output Error: ' : - '❌ Error: '; + data.error_type === 'auth' ? '🔒 Authentication Error: ' : + data.error_type === 'connection' ? '🔌 Connection Error: ' : + data.error_type === 'no_output' ? '📭 No Output Error: ' : + '❌ Error: '; this.commandResult = errorPrefix + data.message; return; } diff --git a/app/templates/index.html b/app/templates/index.html index 29069f5..79407d5 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -38,9 +38,9 @@ leave-to-class="opacity-0">