initial switch commit
This commit is contained in:
		
							
								
								
									
										15
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1 +1,16 @@
 | 
				
			|||||||
 | 
					# Deployment Script
 | 
				
			||||||
deploy_dangrubb.net.sh
 | 
					deploy_dangrubb.net.sh
 | 
				
			||||||
 | 
					# Data files (contain current state)
 | 
				
			||||||
 | 
					server_state.txt
 | 
				
			||||||
 | 
					php/server_state.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Temporary files
 | 
				
			||||||
 | 
					*.tmp
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Logs
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Local configuration
 | 
				
			||||||
 | 
					config.local.php
 | 
				
			||||||
							
								
								
									
										103
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								index.html
									
									
									
									
									
								
							@ -11,6 +11,109 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
 | 
					<!-- Add this anywhere in your HTML -->
 | 
				
			||||||
 | 
					<div id="server-control">
 | 
				
			||||||
 | 
					    <span id="ascii-switch">[||==] OFF</span>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					/* Position the control in top right */
 | 
				
			||||||
 | 
					#server-control {
 | 
				
			||||||
 | 
					    position: fixed;
 | 
				
			||||||
 | 
					    top: 20px;
 | 
				
			||||||
 | 
					    right: 20px;
 | 
				
			||||||
 | 
					    z-index: 1000;
 | 
				
			||||||
 | 
					    font-family: 'Courier New', monospace;
 | 
				
			||||||
 | 
					    font-size: 16px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ascii-switch {
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    user-select: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Color states */
 | 
				
			||||||
 | 
					.state-off {
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.state-on {
 | 
				
			||||||
 | 
					    color: #4CAF50;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.state-unavailable {
 | 
				
			||||||
 | 
					    color: #ff6b6b;
 | 
				
			||||||
 | 
					    cursor: not-allowed;
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					document.addEventListener('DOMContentLoaded', function() {
 | 
				
			||||||
 | 
					    const asciiSwitch = document.getElementById('ascii-switch');
 | 
				
			||||||
 | 
					    let currentState = false; // false = off, true = on
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Load current state
 | 
				
			||||||
 | 
					    fetch('/php/get_state.php')
 | 
				
			||||||
 | 
					        .then(response => response.text())
 | 
				
			||||||
 | 
					        .then(data => {
 | 
				
			||||||
 | 
					            const state = data.trim();
 | 
				
			||||||
 | 
					            currentState = (state === '1');
 | 
				
			||||||
 | 
					            updateDisplay(currentState);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch(() => {
 | 
				
			||||||
 | 
					            // If file doesn't exist or can't be reached, set to unavailable
 | 
				
			||||||
 | 
					            updateDisplay('unavailable');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Handle clicks
 | 
				
			||||||
 | 
					    asciiSwitch.addEventListener('click', function() {
 | 
				
			||||||
 | 
					        if (currentState === 'unavailable') return; // Don't allow clicking if unavailable
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const newState = !currentState;
 | 
				
			||||||
 | 
					        const stateValue = newState ? '1' : '0';
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Optimistically update display
 | 
				
			||||||
 | 
					        updateDisplay(newState);
 | 
				
			||||||
 | 
					        currentState = newState;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Send state to server
 | 
				
			||||||
 | 
					        fetch('/php/update_state.php', {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            headers: {
 | 
				
			||||||
 | 
					                'Content-Type': 'application/x-www-form-urlencoded',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            body: 'state=' + stateValue
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .then(response => response.text())
 | 
				
			||||||
 | 
					        .then(data => {
 | 
				
			||||||
 | 
					            console.log('State updated:', stateValue);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch(error => {
 | 
				
			||||||
 | 
					            console.error('Error updating state:', error);
 | 
				
			||||||
 | 
					            // Revert on error
 | 
				
			||||||
 | 
					            currentState = !newState;
 | 
				
			||||||
 | 
					            updateDisplay(currentState);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    function updateDisplay(state) {
 | 
				
			||||||
 | 
					        asciiSwitch.className = ''; // Clear existing classes
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (state === 'unavailable') {
 | 
				
			||||||
 | 
					            asciiSwitch.textContent = '[????] UNAVAILABLE';
 | 
				
			||||||
 | 
					            asciiSwitch.classList.add('state-unavailable');
 | 
				
			||||||
 | 
					        } else if (state === true || state === '1') {
 | 
				
			||||||
 | 
					            asciiSwitch.textContent = '[==||]⠀ ON';
 | 
				
			||||||
 | 
					            asciiSwitch.classList.add('state-on');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            asciiSwitch.textContent = '[||==] OFF';
 | 
				
			||||||
 | 
					            asciiSwitch.classList.add('state-off');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- partial:index.partial.html -->
 | 
					<!-- partial:index.partial.html -->
 | 
				
			||||||
<div class="bard">
 | 
					<div class="bard">
 | 
				
			||||||
  <div class="strip" style="
 | 
					  <div class="strip" style="
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										45
									
								
								php/get_state.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								php/get_state.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					header('Content-Type: text/plain');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Only allow GET requests
 | 
				
			||||||
 | 
					if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
 | 
				
			||||||
 | 
					    http_response_code(405);
 | 
				
			||||||
 | 
					    die('Method not allowed');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$file_path = '../server_state.txt';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check if file exists
 | 
				
			||||||
 | 
					if (!file_exists($file_path)) {
 | 
				
			||||||
 | 
					    // Return default state if file doesn't exist
 | 
				
			||||||
 | 
					    echo '0';
 | 
				
			||||||
 | 
					    exit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check if file is readable
 | 
				
			||||||
 | 
					if (!is_readable($file_path)) {
 | 
				
			||||||
 | 
					    http_response_code(500);
 | 
				
			||||||
 | 
					    die('Cannot read state file');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$state = file_get_contents($file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate the stored state
 | 
				
			||||||
 | 
					if ($state === false) {
 | 
				
			||||||
 | 
					    http_response_code(500);
 | 
				
			||||||
 | 
					    die('Failed to read state');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean the state (remove any whitespace)
 | 
				
			||||||
 | 
					$state = trim($state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate what we read from the file
 | 
				
			||||||
 | 
					if (!in_array($state, ['0', '1'], true)) {
 | 
				
			||||||
 | 
					    // File was corrupted somehow, reset to default
 | 
				
			||||||
 | 
					    file_put_contents($file_path, '0', LOCK_EX);
 | 
				
			||||||
 | 
					    echo '0';
 | 
				
			||||||
 | 
					    exit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo $state;
 | 
				
			||||||
 | 
					?>
 | 
				
			||||||
							
								
								
									
										66
									
								
								php/update_state.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								php/update_state.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					// Set proper headers
 | 
				
			||||||
 | 
					header('Content-Type: text/plain');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Only allow POST requests
 | 
				
			||||||
 | 
					if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
 | 
				
			||||||
 | 
					    http_response_code(405);
 | 
				
			||||||
 | 
					    die('Method not allowed');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Strict input validation
 | 
				
			||||||
 | 
					if (!isset($_POST['state'])) {
 | 
				
			||||||
 | 
					    http_response_code(400);
 | 
				
			||||||
 | 
					    die('Missing state parameter');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Only allow exact string matches
 | 
				
			||||||
 | 
					if (!in_array($_POST['state'], ['0', '1'], true)) {
 | 
				
			||||||
 | 
					    http_response_code(400);
 | 
				
			||||||
 | 
					    die('Invalid state - must be 0 or 1');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Additional validation - check if it's exactly one character
 | 
				
			||||||
 | 
					if (strlen($_POST['state']) !== 1) {
 | 
				
			||||||
 | 
					    http_response_code(400);
 | 
				
			||||||
 | 
					    die('Invalid state length');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate it's actually a digit
 | 
				
			||||||
 | 
					if (!ctype_digit($_POST['state'])) {
 | 
				
			||||||
 | 
					    http_response_code(400);
 | 
				
			||||||
 | 
					    die('State must be numeric');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$state = $_POST['state'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Use a safe file path outside web root
 | 
				
			||||||
 | 
					$file_path = '../server_state.txt';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ensure directory exists and is writable
 | 
				
			||||||
 | 
					if (!is_writable(dirname($file_path))) {
 | 
				
			||||||
 | 
					    http_response_code(500);
 | 
				
			||||||
 | 
					    die('Server configuration error');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Write with file locking to prevent race conditions
 | 
				
			||||||
 | 
					$result = file_put_contents($file_path, $state, LOCK_EX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ($result === false) {
 | 
				
			||||||
 | 
					    http_response_code(500);
 | 
				
			||||||
 | 
					    die('Failed to write state');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Verify the write was successful
 | 
				
			||||||
 | 
					$written_state = file_get_contents($file_path);
 | 
				
			||||||
 | 
					if ($written_state !== $state) {
 | 
				
			||||||
 | 
					    http_response_code(500);
 | 
				
			||||||
 | 
					    die('State verification failed');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Optional: Log the change (helpful for debugging)
 | 
				
			||||||
 | 
					$log_entry = date('Y-m-d H:i:s') . " - State changed to: {$state} - IP: {$_SERVER['REMOTE_ADDR']}\n";
 | 
				
			||||||
 | 
					error_log($log_entry, 3, '/tmp/server_state.log');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo 'success';
 | 
				
			||||||
 | 
					?>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user