Merge branch 'urvishpatelce-main'

This commit is contained in:
2025-09-03 16:05:59 +02:00
444 changed files with 54536 additions and 311 deletions

View File

@ -1,85 +0,0 @@
<?php
namespace App\Controllers;
use App\Services\LxdService;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class HandoffController
{
public function post(ServerRequestInterface $req, ResponseInterface $res): ResponseInterface
{
// if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); }
$q = $req->getQueryParams();
$name = (string)($q['name'] ?? '');
$id = (string)($q['handoff'] ?? '');
$path = (string)($q['path'] ?? '/login');
if (!$name || !$id) {
return $this->html($res, 400, '<h1>Bad request</h1>');
}
$lxd = new LxdService();
$ip = $lxd->getContainerIP($name);
if (!$ip) {
return $this->html($res, 503, '<h1>Container not ready</h1>');
}
$key = "handoff:$name:$id";
$data = $_SESSION[$key] ?? null;
unset($_SESSION[$key]); // one-time use
if (!$data || empty($data['username']) || empty($data['password'])) {
return $this->html($res, 410, '<h1>Handoff expired</h1>');
}
// Restrict to relative paths
$path = $this->sanitizePath($path);
// If your container has TLS, prefer https://
$action = 'http://' . $ip . $path;
$html = <<<HTML
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Signing you in…</title></head>
<body>
<form id="f" method="POST" action="{$this->e($action)}">
<input type="hidden" name="username" value="{$this->e($data['username'])}">
<input type="hidden" name="password" value="{$this->e($data['password'])}">
</form>
<script>document.getElementById('f').submit();</script>
<noscript>
<p>JavaScript is required to continue. Click the button below.</p>
<button form="f" type="submit">Continue</button>
</noscript>
</body>
</html>
HTML;
return $this->html($res, 200, $html);
}
private function e(string $s): string
{
return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
private function html(ResponseInterface $res, int $code, string $html): ResponseInterface
{
$res->getBody()->write($html);
return $res->withHeader('Content-Type', 'text/html; charset=utf-8')->withStatus($code);
}
private function sanitizePath(string $raw): string
{
if (preg_match('#^https?://#i', $raw)) return '/login';
if (!str_starts_with($raw, '/')) return '/login';
$path = parse_url($raw, PHP_URL_PATH) ?? '/login';
$allow = ['/', '/login', '/signin'];
return in_array($path, $allow, true) ? $path : '/login';
}
}

View File

@ -7,6 +7,7 @@ use App\Services\LxdService;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use App\Utils\LogWriterHelper;
use App\Utils\ContainerHelper;
class LoginController
{
@ -16,12 +17,7 @@ class LoginController
public function index(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
try {
$origin = $request->getHeaderLine('Origin');
$domain = !empty($origin) ? parse_url($origin, PHP_URL_HOST) : $request->getHeaderLine('Host');
$configPath = __DIR__ . '/../../config.json';
$config = file_exists($configPath) ? json_decode(file_get_contents($configPath), true) : [];
$name = $config[$domain] ?? null;
$name = ContainerHelper::getName($request);
$params = (array)$request->getParsedBody();
@ -42,31 +38,12 @@ class LoginController
if ($status !== 'Running') {
$lxd->startContainer($name);
}
// Log path only (avoid leaking query in logs)
$uri = $request->getUri();
LogWriterHelper::write($name, $uri->getPath());
// ---- NEW: create one-time handoff and store creds server-side ----
$handoffId = bin2hex(random_bytes(16));
$_SESSION["handoff:$name:$handoffId"] = [
'username' => (string)($params['username'] ?? ''),
'password' => (string)($params['password'] ?? ''),
'created_at' => time(),
];
// sanitize the container path (not a full URL!)
$path = $this->sanitizeRedirectPath($params['redirect'] ?? '/login');
// Client goes to waiting page; when ready it will be sent to the bridge
$redirect = '/waiting?name=' . rawurlencode($name)
. '&handoff=' . rawurlencode($handoffId)
. '&path=' . rawurlencode($path);
LogWriterHelper::write($name);
return $this->json($response, [
'status' => 'success',
'message' => 'Container started!',
'redirect' => $redirect
'message' => 'Container gestartet!'
]);
} catch (\Throwable $e) {
return $this->json($response, [
@ -78,8 +55,7 @@ class LoginController
public function status(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$queryParams = $request->getQueryParams();
$name = $queryParams['name'] ?? '';
$name = ContainerHelper::getName($request);
if (empty($name)) {
return $this->json($response, [
@ -115,6 +91,7 @@ class LoginController
return $this->json($response, [
'status' => 'ready',
'ip' => $ip,
'name' => $name,
'message' => 'Container is ready',
]);
}

View File

@ -8,7 +8,7 @@ use App\Services\LxdService;
$lxdService = new LxdService();
// Define the directory containing access logs
$logDir = realpath(__DIR__ . '/../../public/last-access-logs'); // Adjust if you're in /app/src/Cron or similar
$logDir = $_ENV['STATEDIR'] ?? "/var/www/html/lxd-app/api/public/last-access-logs";
// Define the idle threshold in minutes
$thresholdMinutes = 30;
@ -18,23 +18,15 @@ foreach (glob($logDir . '/*.txt') as $filePath) {
// Extract the container name from the file name
$containerName = basename($filePath, '.txt');
// Get the last line from the log file
$lastLine = getLastLine($filePath);
if (!$lastLine) {
echo "No access logs found for $containerName.\n";
// Get the last modified time of the file
$lastModified = filemtime($filePath);
if (!$lastModified) {
echo "Failed to get modification time for $containerName.\n";
continue;
}
// Parse the timestamp from the last log entry
$parts = explode(' : ', $lastLine);
if (!isset($parts[0])) continue;
$lastAccess = DateTime::createFromFormat('Y-m-d H:i:s', trim($parts[0]));
if (!$lastAccess) continue;
// Calculate the idle time in seconds
$now = new DateTime();
$interval = $now->getTimestamp() - $lastAccess->getTimestamp();
$now = time();
$interval = $now - $lastModified;
// Check if the container has been idle for longer than the threshold
if ($interval > $thresholdMinutes * 60) {
@ -49,20 +41,7 @@ foreach (glob($logDir . '/*.txt') as $filePath) {
echo "Container $containerName does not exist.\n";
}
} catch (Throwable $e) {
// Handle any errors that occur while stopping the container
echo "Error stopping $containerName: " . $e->getMessage() . "\n";
}
}
}
/**
* Get the last non-empty line from a file.
*
* @param string $filePath Path to the file.
* @return string|null The last line, or null if the file is empty.
*/
function getLastLine(string $filePath): ?string
{
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
return $lines ? end($lines) : null;
}

View File

@ -36,7 +36,7 @@ class LxdService
curl_setopt($ch, CURLOPT_SSLCERT, $clientCert);
curl_setopt($ch, CURLOPT_SSLKEY, $clientKey);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

View File

@ -0,0 +1,33 @@
<?php
namespace App\Utils;
use Psr\Http\Message\ServerRequestInterface;
use Illuminate\Support\Facades\File;
class ContainerHelper
{
public static function getName(ServerRequestInterface $request): ?string
{
// Try to get Origin header, fallback to Host
$origin = $request->getHeaderLine('Origin');
$domain = !empty($origin) ? parse_url($origin, PHP_URL_HOST) : $request->getHeaderLine('Host');
// Load config.json once
$config = self::getConfig();
return $config[$domain] ?? null;
}
protected static function getConfig(): array
{
static $config = null;
if ($config !== null) {
return $config;
}
$configPath = __DIR__ . '/../../config.json';
return file_exists($configPath) ? json_decode(file_get_contents($configPath), true) : [];
}
}

View File

@ -3,19 +3,7 @@
namespace App\Utils;
class LogWriterHelper {
public static function write(string $name, string $uri): void {
// Dynamically resolve the log directory relative to the current file
$logDir = realpath(__DIR__ . '/../../public/last-access-logs');
// If the resolved path doesn't exist (e.g., public dir was missing), create it
if (!$logDir) {
$logDir = __DIR__ . '/../../public/last-access-logs';
if (!file_exists($logDir)) {
mkdir($logDir, 0777, true);
}
}
$logLine = date("Y-m-d H:i:s") . " : " . $uri . "\n";
file_put_contents($logDir . '/' . $name . '.txt', $logLine, FILE_APPEND);
public static function write(string $name): void {
touch($_ENV['STATEDIR']."/".$name);
}
}