feat(php): update live

typescript_changes
flop 3 weeks ago
parent 9e31555d6f
commit 178243a9cc
  1. 129
      ts/live.php

@ -5,25 +5,23 @@ define('PASSWORD', 'cttue_avj2305_44');
define('FILES_DIR', __DIR__ . '/avj2305'); define('FILES_DIR', __DIR__ . '/avj2305');
define('ACTIVE_FILE', __DIR__ . '/active.txt'); define('ACTIVE_FILE', __DIR__ . '/active.txt');
// -- helpers ------------------------------------------------------------------ // =============================================================================
// HELPERS
// =============================================================================
function is_authed(): bool { function redirect(string $url): never {
return !empty($_SESSION['auth']); header('Location: ' . $url);
exit;
} }
function require_auth(): void { function require_auth(): void {
if (!is_authed()) { if (empty($_SESSION['auth'])) redirect('?login');
header('Location: ?login');
exit;
}
} }
function get_active(): string { function get_active(): string {
if (file_exists(ACTIVE_FILE)) { if (!file_exists(ACTIVE_FILE)) return '';
$v = trim(file_get_contents(ACTIVE_FILE)); $v = trim(file_get_contents(ACTIVE_FILE));
if ($v !== '') return $v; return $v !== '' ? $v : '';
}
return '';
} }
function set_active(string $filename): void { function set_active(string $filename): void {
@ -32,45 +30,42 @@ function set_active(string $filename): void {
function get_bin_files(): array { function get_bin_files(): array {
$files = glob(FILES_DIR . '/*.bin'); $files = glob(FILES_DIR . '/*.bin');
if (!$files) return []; return $files ? array_map('basename', $files) : [];
return array_map('basename', $files);
} }
function safe_filename(string $name): bool { function safe_filename(string $name): bool {
// basename only, no path traversal, must end in .bin
return $name === basename($name) return $name === basename($name)
&& str_ends_with($name, '.bin') && str_ends_with($name, '.bin')
&& !str_contains($name, "\0"); && !str_contains($name, "\0");
} }
// -- routing ------------------------------------------------------------------ // =============================================================================
// ACTIONS (POST handlers - redirect, never render)
// =============================================================================
$q = array_key_first($_GET) ?? ''; // first query param key function action_login(): never {
// POST: login
if ($q === 'login' && $_SERVER['REQUEST_METHOD'] === 'POST') {
if (hash_equals(PASSWORD, $_POST['password'] ?? '')) { if (hash_equals(PASSWORD, $_POST['password'] ?? '')) {
$_SESSION['auth'] = true; $_SESSION['auth'] = true;
header('Location: ?edit'); redirect('?edit');
} else {
header('Location: ?login&err=1');
} }
exit; redirect('?login&err=1');
} }
// POST: set active file function action_set_file(): never {
if ($q === 'edit' && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['file'])) {
require_auth(); require_auth();
$f = $_POST['file']; $f = $_POST['file'] ?? '';
if (safe_filename($f) && file_exists(FILES_DIR . '/' . $f)) { if (safe_filename($f) && file_exists(FILES_DIR . '/' . $f)) {
set_active($f); set_active($f);
} }
header('Location: ?edit'); redirect('?edit');
exit;
} }
// GET: login page // =============================================================================
if ($q === 'login') { ?> // RENDERERS (GET handlers - output HTML, never redirect)
// =============================================================================
function render_login(): never {
$err = !empty($_GET['err']); ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head><meta charset="utf-8"><title>Login</title> <head><meta charset="utf-8"><title>Login</title>
@ -88,17 +83,15 @@ if ($q === 'login') { ?>
<label>Password</label> <label>Password</label>
<input type="password" name="password" autofocus> <input type="password" name="password" autofocus>
<button type="submit">Login</button> <button type="submit">Login</button>
<?php if (!empty($_GET['err'])): ?><span class="err">Wrong password.</span><?php endif; ?> <?php if ($err): ?><span class="err">Wrong password.</span><?php endif; ?>
</form> </form>
</body></html> </body></html>
<?php exit; } <?php exit; }
// GET: edit page function render_edit(): never {
if ($q === 'edit') {
require_auth(); require_auth();
$files = get_bin_files(); $files = get_bin_files();
$active = get_active(); $active = get_active(); ?>
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head><meta charset="utf-8"><title>Select File</title> <head><meta charset="utf-8"><title>Select File</title>
@ -122,7 +115,7 @@ if ($q === 'edit') {
<li> <li>
<form method="post" action="?edit"> <form method="post" action="?edit">
<input type="hidden" name="file" value="<?= htmlspecialchars($f) ?>"> <input type="hidden" name="file" value="<?= htmlspecialchars($f) ?>">
<button type="submit" <?= $f === $active ? 'class="active"' : '' ?>><?= htmlspecialchars($f) ?></button> <button type="submit"<?= $f === $active ? ' class="active"' : '' ?>><?= htmlspecialchars($f) ?></button>
</form> </form>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
@ -133,27 +126,53 @@ if ($q === 'edit') {
</body></html> </body></html>
<?php exit; } <?php exit; }
// DEFAULT: serve active .bin file // =============================================================================
$active = get_active(); // FILE SERVER (default route)
// =============================================================================
if ($active === '') { function serve_active_file(): never {
http_response_code(404); $active = get_active();
exit('No active file set.');
}
if (!safe_filename($active)) { if ($active === '') {
http_response_code(500); http_response_code(404); exit('No active file set.');
exit('Invalid filename.'); }
} if (!safe_filename($active)) {
http_response_code(500); exit('Invalid filename.');
}
$path = FILES_DIR . '/' . $active; $path = FILES_DIR . '/' . $active;
if (!file_exists($path)) { if (!file_exists($path)) {
http_response_code(404); http_response_code(404); exit('File not found.');
exit('File not found.'); }
header('Content-Type: application/octet-stream');
header('Content-Disposition: inline; filename="' . addslashes($active) . '"');
header('Content-Length: ' . filesize($path));
readfile($path);
exit;
} }
header('Content-Type: application/octet-stream');
header('Content-Disposition: inline; filename="' . addslashes($active) . '"'); // =============================================================================
header('Content-Length: ' . filesize($path)); // ROUTES
readfile($path); // =============================================================================
//
// GET /live.php -> serve active .bin file
// GET /live.php?login -> login page
// POST /live.php?login -> process login
// GET /live.php?edit -> file picker [auth required]
// POST /live.php?edit -> set active file [auth required]
//
// =============================================================================
$method = $_SERVER['REQUEST_METHOD'];
$route = array_key_first($_GET) ?? '';
match (true) {
$method === 'POST' && $route === 'login' => action_login(),
$method === 'POST' && $route === 'edit' => action_set_file(),
$method === 'GET' && $route === 'login' => render_login(),
$method === 'GET' && $route === 'edit' => render_edit(),
default => serve_active_file(),
};

Loading…
Cancel
Save