initial switch commit
This commit is contained in:
15
.gitignore
vendored
15
.gitignore
vendored
@ -1 +1,16 @@
|
||||
# Deployment Script
|
||||
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>
|
||||
<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 -->
|
||||
<div class="bard">
|
||||
<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