forked from urvishpatelce/lxd-app
Compare commits
9 Commits
481508b88c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 19bdd41664 | |||
| b99533ced8 | |||
| d5ecf0da96 | |||
| fb51cd6e29 | |||
| 65b6f45753 | |||
| cfabf3c9f5 | |||
| 4072377548 | |||
| b96a1c0065 | |||
| c05d98f333 |
5
add-container.php
Normal file
5
add-container.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
if (count($argv) !== 3) return;
|
||||||
|
$config = json_decode(file_get_contents(__DIR__ . '/api/config.json'), true);
|
||||||
|
$config[$argv[1]] = $argv[2];
|
||||||
|
file_put_contents(__DIR__ . '/api/config.json', json_encode($config));
|
||||||
@ -16,7 +16,6 @@ require __DIR__ . '/../vendor/autoload.php';
|
|||||||
$dotenv = Dotenv::createImmutable(__DIR__ . '/../');
|
$dotenv = Dotenv::createImmutable(__DIR__ . '/../');
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
|
||||||
$domain = $_ENV['MAIN_COOKIE_DOMAIN'] ?? '.lxdapp.local';
|
|
||||||
session_set_cookie_params([
|
session_set_cookie_params([
|
||||||
'lifetime' => 0,
|
'lifetime' => 0,
|
||||||
'path' => '/',
|
'path' => '/',
|
||||||
@ -33,7 +32,6 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
$containerBuilder = new ContainerBuilder();
|
$containerBuilder = new ContainerBuilder();
|
||||||
$containerBuilder->useAutowiring(true); // Enable autowiring globally
|
$containerBuilder->useAutowiring(true); // Enable autowiring globally
|
||||||
|
|
||||||
|
|
||||||
// Add settings
|
// Add settings
|
||||||
$settings = require __DIR__ . '/../src/Settings/Settings.php';
|
$settings = require __DIR__ . '/../src/Settings/Settings.php';
|
||||||
$settings($containerBuilder);
|
$settings($containerBuilder);
|
||||||
@ -58,7 +56,6 @@ $app->add(CorsMiddleware::class);
|
|||||||
// Register middleware
|
// Register middleware
|
||||||
(require __DIR__ . '/../src/Bootstrap/Middleware.php')($app);
|
(require __DIR__ . '/../src/Bootstrap/Middleware.php')($app);
|
||||||
|
|
||||||
|
|
||||||
// Register routes
|
// Register routes
|
||||||
|
|
||||||
// API contianer proxy route
|
// API contianer proxy route
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use Slim\App;
|
use Slim\App;
|
||||||
use Slim\Middleware\ErrorMiddleware;
|
use Slim\Middleware\ErrorMiddleware;
|
||||||
use Slim\Middleware\BodyParsingMiddleware;
|
|
||||||
|
|
||||||
return function (App $app) {
|
return function (App $app) {
|
||||||
// Add body parsing middleware (for JSON, form, etc.)
|
// Add body parsing middleware (for JSON, form, etc.)
|
||||||
|
|||||||
@ -77,20 +77,18 @@ class LoginController
|
|||||||
$status = $state['metadata']['status'] ?? 'Stopped';
|
$status = $state['metadata']['status'] ?? 'Stopped';
|
||||||
if ($status !== 'Running') {
|
if ($status !== 'Running') {
|
||||||
return $this->json($response, [
|
return $this->json($response, [
|
||||||
'status' => 'starting',
|
'status' => 'stopped',
|
||||||
'message' => 'Container is not yet running',
|
'message' => 'Container is not yet running',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ip = $lxd->getContainerIP($name);
|
|
||||||
$nginx = $lxd->getContainerServiceStatus($name, 'nginx');
|
$nginx = $lxd->getContainerServiceStatus($name, 'nginx');
|
||||||
$mysql = $lxd->getContainerServiceStatus($name, 'mariadb');
|
$mysql = $lxd->getContainerServiceStatus($name, 'mariadb');
|
||||||
|
|
||||||
if ($ip && $nginx === 'active' && $mysql === 'active') {
|
if ($nginx === 'active' && $mysql === 'active') {
|
||||||
// ---- CHANGED: do NOT return fields/creds here ----
|
// ---- CHANGED: do NOT return fields/creds here ----
|
||||||
return $this->json($response, [
|
return $this->json($response, [
|
||||||
'status' => 'ready',
|
'status' => 'ready',
|
||||||
'ip' => $ip,
|
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'message' => 'Container is ready',
|
'message' => 'Container is ready',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require __DIR__ . '/../../vendor/autoload.php'; // Adjust path as needed
|
|
||||||
|
|
||||||
use App\Services\LxdService;
|
|
||||||
|
|
||||||
// Initialize LXD service
|
|
||||||
$lxdService = new LxdService();
|
|
||||||
|
|
||||||
// Define the directory containing access logs
|
|
||||||
$logDir = $_ENV['STATEDIR'] ?? "/var/www/html/lxd-app/api/public/last-access-logs";
|
|
||||||
|
|
||||||
// Define the idle threshold in minutes
|
|
||||||
$thresholdMinutes = 30;
|
|
||||||
|
|
||||||
// Iterate over all log files in the specified directory
|
|
||||||
foreach (glob($logDir . '/*.txt') as $filePath) {
|
|
||||||
// Extract the container name from the file name
|
|
||||||
$containerName = basename($filePath, '.txt');
|
|
||||||
|
|
||||||
// Get the last modified time of the file
|
|
||||||
$lastModified = filemtime($filePath);
|
|
||||||
if (!$lastModified) {
|
|
||||||
echo "Failed to get modification time for $containerName.\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$now = time();
|
|
||||||
$interval = $now - $lastModified;
|
|
||||||
|
|
||||||
// Check if the container has been idle for longer than the threshold
|
|
||||||
if ($interval > $thresholdMinutes * 60) {
|
|
||||||
echo "$containerName has been idle for over $thresholdMinutes minutes. Stopping...\n";
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if the container exists and stop it if it does
|
|
||||||
if ($lxdService->containerExists($containerName)) {
|
|
||||||
$lxdService->stopContainer($containerName);
|
|
||||||
echo "Stopped container: $containerName\n";
|
|
||||||
} else {
|
|
||||||
echo "Container $containerName does not exist.\n";
|
|
||||||
}
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
echo "Error stopping $containerName: " . $e->getMessage() . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,23 +10,8 @@ class LxdService
|
|||||||
$this->baseUrl = $_ENV['LXD_API_URL'] ?? 'https://localhost:8443';
|
$this->baseUrl = $_ENV['LXD_API_URL'] ?? 'https://localhost:8443';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function curlInit($url, $method = '') {
|
||||||
|
$ch = curl_init($url);
|
||||||
/**
|
|
||||||
* Sends an HTTP request to the LXD API.
|
|
||||||
*
|
|
||||||
* @param string $method HTTP method (GET, POST, PUT, etc.)
|
|
||||||
* @param string $endpoint API endpoint
|
|
||||||
* @param array $body Request body (optional)
|
|
||||||
* @return array Response from the API
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function request(string $method, string $endpoint, array $body = []): array {
|
|
||||||
// if (!isset($_ENV['LXD_CLIENT_CERT'], $_ENV['LXD_CLIENT_KEY'])) {
|
|
||||||
// throw new \Exception("LXD_CLIENT_CERT and LXD_CLIENT_KEY must be set in .env");
|
|
||||||
// }
|
|
||||||
|
|
||||||
$ch = curl_init("{$this->baseUrl}{$endpoint}");
|
|
||||||
|
|
||||||
// Paths to client certificate and key for TLS authentication
|
// Paths to client certificate and key for TLS authentication
|
||||||
$clientCert = $_ENV['LXD_CLIENT_CERT'] ?? '/etc/ssl/lxdapp/client.crt';
|
$clientCert = $_ENV['LXD_CLIENT_CERT'] ?? '/etc/ssl/lxdapp/client.crt';
|
||||||
@ -38,13 +23,25 @@ class LxdService
|
|||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
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_RETURNTRANSFER, true);
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
if($method) curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||||
|
return $ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an HTTP request to the LXD API.
|
||||||
|
*
|
||||||
|
* @param string $method HTTP method (GET, POST, PUT, etc.)
|
||||||
|
* @param string $endpoint API endpoint
|
||||||
|
* @param array $body Request body (optional)
|
||||||
|
* @return array Response from the API
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function request(string $method, string $endpoint, array $body = []): array {
|
||||||
|
$ch = $this->curlInit("{$this->baseUrl}{$endpoint}", $method);
|
||||||
if (!empty($body)) {
|
if (!empty($body)) {
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
$response = curl_exec($ch);
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
$httpCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||||
|
|
||||||
@ -77,17 +74,7 @@ class LxdService
|
|||||||
*/
|
*/
|
||||||
private function requestRaw(string $endpoint): string {
|
private function requestRaw(string $endpoint): string {
|
||||||
$url = "{$this->baseUrl}{$endpoint}";
|
$url = "{$this->baseUrl}{$endpoint}";
|
||||||
$ch = curl_init($url);
|
$ch = $this->curlInit($url);
|
||||||
|
|
||||||
$clientCert = $_ENV['LXD_CLIENT_CERT'] ?? '/etc/ssl/lxdapp/client.crt';
|
|
||||||
$clientKey = $_ENV['LXD_CLIENT_KEY'] ?? '/etc/ssl/lxdapp/client.key';
|
|
||||||
|
|
||||||
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_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
$response = curl_exec($ch);
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
$httpCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<Spinner :loading="loading" />
|
<Spinner :loading="loading" />
|
||||||
<div v-html="output" />
|
<div v-html="output" />
|
||||||
<div v-if="!loading && status ==='starting'">
|
<div v-show="!loading && status ==='stopped'">
|
||||||
<form name="loginForm" method="post" action="/login.php" @submit.prevent="submitForm" class="login-form" autocomplete="on">
|
<form name="loginForm" id="loginForm" method="post" action="/login.php" @submit.prevent="submitForm" class="login-form" autocomplete="on">
|
||||||
<input type="hidden" name="relocation" value="route=search/search&type=simple" />
|
<input type="hidden" name="relocation" value="route=search/search&type=simple" />
|
||||||
|
|
||||||
<h2 class="title">Login</h2>
|
<h2 class="title">Login</h2>
|
||||||
@ -34,14 +34,15 @@
|
|||||||
<Captcha ref="captchaRef" v-model:captcha="captchaValue" />
|
<Captcha ref="captchaRef" v-model:captcha="captchaValue" />
|
||||||
<button type="submit" :disabled="loading || !captchaValue" class="btn">
|
<button type="submit" :disabled="loading || !captchaValue" class="btn">
|
||||||
<span v-if="!loading">Login</span>
|
<span v-if="!loading">Login</span>
|
||||||
<span v-else>Loading...</span>
|
<span v-else>Lade Archiv...</span>
|
||||||
</button>
|
</button>
|
||||||
<p v-if="captchaError" class="error-text">{{ captchaError }}</p>
|
<p v-if="captchaError" class="error-text">{{ captchaError }}</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="waiting-container">
|
<div v-if="loading || status !== 'stopped'" class="waiting-container">
|
||||||
<h2>Container Status: {{ status }}</h2>
|
<h2>Status Mailarchiv</h2>
|
||||||
<p v-if="status === 'running'">Waiting for services to be ready...</p>
|
<p v-if="status === 'ready'">Archiv ist bereit, sie werden weitergeleitet...</p>
|
||||||
|
<p v-else-if="status === 'running'">Zugriff auf Archiv wird vorbereitet...</p>
|
||||||
<p v-else-if="status === 'error'" class="error-text">{{ errorMessage }}</p>
|
<p v-else-if="status === 'error'" class="error-text">{{ errorMessage }}</p>
|
||||||
</div>
|
</div>
|
||||||
<Spinner :loading="loading" />
|
<Spinner :loading="loading" />
|
||||||
@ -49,7 +50,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
definePageMeta({
|
/*definePageMeta({
|
||||||
// This is an example of inline middleware
|
// This is an example of inline middleware
|
||||||
middleware: async nuxtApp => {
|
middleware: async nuxtApp => {
|
||||||
const host = useRequestURL().host
|
const host = useRequestURL().host
|
||||||
@ -60,7 +61,7 @@ definePageMeta({
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
})
|
})*/
|
||||||
|
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
@ -68,8 +69,6 @@ import Spinner from '@/components/Spinner.vue'
|
|||||||
import Captcha from '@/components/Captcha.vue'
|
import Captcha from '@/components/Captcha.vue'
|
||||||
|
|
||||||
const output = ref('')
|
const output = ref('')
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const username = ref('')
|
const username = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
@ -79,6 +78,7 @@ const captchaError = ref('')
|
|||||||
const allowAccess = ref(false)
|
const allowAccess = ref(false)
|
||||||
const interval = ref(null)
|
const interval = ref(null)
|
||||||
const status = ref('starting')
|
const status = ref('starting')
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
const POLL_MS = 5000
|
const POLL_MS = 5000
|
||||||
const ALLOWED_REDIRECTS = new Set(['/', '/login', '/signin'])
|
const ALLOWED_REDIRECTS = new Set(['/', '/login', '/signin'])
|
||||||
@ -95,7 +95,7 @@ async function checkStatus () {
|
|||||||
if (interval.value) clearInterval(interval.value)
|
if (interval.value) clearInterval(interval.value)
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (username.value && password.value) {
|
if (username.value && password.value) {
|
||||||
document.loginForm.submit()
|
document.getElementById('loginForm').submit()
|
||||||
} else {
|
} else {
|
||||||
navigateTo(window.location.origin + '/', { external: true })
|
navigateTo(window.location.origin + '/', { external: true })
|
||||||
}
|
}
|
||||||
@ -103,6 +103,7 @@ async function checkStatus () {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
status.value = 'error'
|
status.value = 'error'
|
||||||
|
console.log(error)
|
||||||
errorMessage.value = error?.data?.message || 'Internal server error!'
|
errorMessage.value = error?.data?.message || 'Internal server error!'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,9 +135,10 @@ const submitForm = async () => {
|
|||||||
},
|
},
|
||||||
throwHttpErrors: false
|
throwHttpErrors: false
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res?.status !== 'success') {
|
if (res?.status !== 'success') {
|
||||||
captchaError.value = res?.message || 'Login failed'
|
captchaError.value = res?.message || 'Login failed'
|
||||||
|
} else {
|
||||||
|
status.value = 'starting'
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
captchaError.value = error?.data?.message || 'Internal server error!'
|
captchaError.value = error?.data?.message || 'Internal server error!'
|
||||||
|
|||||||
@ -27,7 +27,6 @@ $lxd = new LxdService();
|
|||||||
|
|
||||||
// === Helper URLs ===
|
// === Helper URLs ===
|
||||||
$redirectBase = parseUrl("$host/app");
|
$redirectBase = parseUrl("$host/app");
|
||||||
$waitingPage = parseUrl("$host/app/waiting?name=$container&redirect=" . urlencode(getFullUrl()));
|
|
||||||
// === If container is missing or invalid ===
|
// === If container is missing or invalid ===
|
||||||
if (!$container || !$lxd->containerExists($container)) {
|
if (!$container || !$lxd->containerExists($container)) {
|
||||||
redirect($redirectBase);
|
redirect($redirectBase);
|
||||||
@ -41,13 +40,11 @@ if ($state !== 'Running') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// === Get container IP ===
|
// === Get container IP ===
|
||||||
$ip = $lxd->getContainerIP($container);
|
|
||||||
|
|
||||||
$nginx = $lxd->getContainerServiceStatus($container, 'nginx');
|
$nginx = $lxd->getContainerServiceStatus($container, 'nginx');
|
||||||
$mysql = $lxd->getContainerServiceStatus($container, 'mariadb');
|
$mysql = $lxd->getContainerServiceStatus($container, 'mariadb');
|
||||||
|
|
||||||
if (!$ip || $nginx !== 'active' || $mysql !== 'active') {
|
if ($nginx !== 'active' || $mysql !== 'active') {
|
||||||
redirect($waitingPage);
|
redirect($redirectBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Proxy to container ===
|
// === Proxy to container ===
|
||||||
|
|||||||
Reference in New Issue
Block a user