345 lines
14 KiB
PHP
345 lines
14 KiB
PHP
@extends('frontend.layouts.master')
|
|
|
|
@push('viewStyles')
|
|
<link rel="stylesheet" href="{{ asset('lib/wmks/css/wmks-all.css') }}" />
|
|
<link rel="stylesheet" href="{{ asset('lib/adminlte/adminlte.css') }}">
|
|
<style>
|
|
.console-container {
|
|
background: #1a1a1a;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.console-header {
|
|
background: #2a2a2a;
|
|
border-bottom: 1px solid #3a3a3a;
|
|
padding: 12px 20px;
|
|
}
|
|
|
|
.control-panel {
|
|
background: white;
|
|
border-radius: 8px;
|
|
border: 1px solid #e5e7eb;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #3b82f6;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
font-weight: 500;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: #2563eb;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #f8f9fa;
|
|
color: #374151;
|
|
border: 1px solid #d1d5db;
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
font-weight: 500;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #e5e7eb;
|
|
}
|
|
|
|
.input-field {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
transition: border-color 0.2s;
|
|
}
|
|
|
|
.input-field:focus {
|
|
outline: none;
|
|
border-color: #3b82f6;
|
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 32px;
|
|
height: 32px;
|
|
border: 3px solid #e5e7eb;
|
|
border-top: 3px solid #3b82f6;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #111827;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
}
|
|
|
|
.status-connecting { background: #f59e0b; }
|
|
.status-connected { background: #10b981; }
|
|
.status-error { background: #ef4444; }
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div class="min-h-screen bg-gray-50">
|
|
<div class="container mx-auto px-6 py-8">
|
|
|
|
<!-- Main Layout: 2 Columns -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
|
|
|
<!-- Console Display - Main Column (3/4) -->
|
|
<div class="lg:col-span-3">
|
|
<div class="console-container">
|
|
<!-- Console Header -->
|
|
<div class="console-header">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="text-white">
|
|
<h3 class="font-medium text-base">VM Console</h3>
|
|
<p class="text-sm text-gray-400">{{ $product->name ?? 'Virtual Machine' }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center space-x-3">
|
|
<span class="status-dot status-connecting" id="statusDot"></span>
|
|
<span class="text-sm text-gray-400" id="statusText">Connecting...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loading State -->
|
|
<div id="loadingState" class="w-full h-[70vh] flex items-center justify-center bg-gray-900">
|
|
<div class="text-center">
|
|
<div class="loading-spinner mx-auto mb-4"></div>
|
|
<h3 class="text-xl font-medium text-white mb-2">Connecting to vCenter</h3>
|
|
<p class="text-gray-400">Please wait...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Console Display -->
|
|
<div id="wmksContainer" class="w-full h-[70vh] relative bg-black hidden">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Control Panel - Sidebar (1/4) -->
|
|
<div class="lg:col-span-1">
|
|
<div class="space-y-6">
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="control-panel p-6">
|
|
<h3 class="section-title">Control Panel</h3>
|
|
|
|
<div class="space-y-3">
|
|
<button onclick="enterFullScreen()"
|
|
class="btn-secondary w-full flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"/>
|
|
</svg>
|
|
Full Screen
|
|
</button>
|
|
|
|
<button onclick="sendCAD()"
|
|
class="btn-secondary w-full flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
</svg>
|
|
Ctrl + Alt + Del
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Text Input -->
|
|
<div class="control-panel p-6">
|
|
<h4 class="section-title">Send Text</h4>
|
|
|
|
<div class="space-y-3">
|
|
<input type="text"
|
|
id="sendStringText"
|
|
placeholder="Type your message..."
|
|
class="input-field"/>
|
|
|
|
<button onclick="sendInputString()"
|
|
class="btn-primary w-full flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>
|
|
</svg>
|
|
Send
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Help -->
|
|
<div class="control-panel p-6">
|
|
<h4 class="section-title">Keyboard Shortcuts</h4>
|
|
|
|
<div class="space-y-2 text-sm text-gray-600">
|
|
<div class="flex justify-between">
|
|
<span>Full Screen</span>
|
|
<code class="bg-gray-100 px-2 py-1 rounded text-xs">F11</code>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span>Send Text</span>
|
|
<code class="bg-gray-100 px-2 py-1 rounded text-xs">Ctrl+Enter</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="{{ asset('lib/jquery/jquery.min.js') }}"></script>
|
|
<script src="{{ asset('lib/jquery-ui/jquery-ui.min.js') }}"></script>
|
|
<script src="{{ asset('lib/wmks/wmks.min.js') }}"></script>
|
|
<script src="{{ asset('lib/wmks/wmks-sdk-example.js') }}"></script>
|
|
|
|
<script type="text/javascript">
|
|
// Show loading state initially
|
|
$(document).ready(function() {
|
|
$('#loadingState').removeClass('hidden');
|
|
$('#wmksContainer').addClass('hidden');
|
|
});
|
|
|
|
let WMKSContainer = $('#wmksContainer');
|
|
var wmks = WMKS.createWMKS("wmksContainer", {})
|
|
.register(WMKS.CONST.Events.CONNECTION_STATE_CHANGE, function(event, data) {
|
|
if (data.state === WMKS.CONST.ConnectionState.CONNECTED) {
|
|
console.log("Connected successfully");
|
|
|
|
// Update status
|
|
$('#statusDot').removeClass('status-connecting').addClass('status-connected');
|
|
$('#statusText').text('Connected');
|
|
|
|
// Hide loading and show console
|
|
$('#loadingState').addClass('hidden');
|
|
$('#wmksContainer').removeClass('hidden');
|
|
} else if (data.state === WMKS.CONST.ConnectionState.DISCONNECTED) {
|
|
$('#statusDot').removeClass('status-connecting status-connected').addClass('status-error');
|
|
$('#statusText').text('Disconnected');
|
|
}
|
|
});
|
|
|
|
const getTicket = new Promise((resolve, reject) => {
|
|
$.ajax({
|
|
url: '{{ route('api.GetConsoleTicket') }}',
|
|
type: 'get',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
'Authorization': 'Bearer {{ request()->cookie('aut_accessToken') }}'
|
|
},
|
|
data: {
|
|
product: {{ $product->id }}
|
|
},
|
|
success: function(response) {
|
|
if (response.error === false) {
|
|
resolve(response)
|
|
} else {
|
|
reject(response.message)
|
|
}
|
|
}
|
|
})
|
|
});
|
|
|
|
getTicket
|
|
.then(result => {
|
|
if (result !== false) {
|
|
const wmksHost = '{{ config('vmware.wmks_host', '10.10.10.120') }}';
|
|
wmks.connect(`wss://${wmksHost}/ticket/${result.data}`);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log(`Connection error: ${error}`)
|
|
|
|
// Update status
|
|
$('#statusDot').removeClass('status-connecting').addClass('status-error');
|
|
$('#statusText').text('Connection Failed');
|
|
|
|
// Hide loading state and show error
|
|
$('#loadingState').addClass('hidden');
|
|
$('#wmksContainer').removeClass('hidden');
|
|
|
|
WMKSContainer.html(`
|
|
<div class="flex items-center justify-center w-full h-full bg-gray-900">
|
|
<div class="text-center">
|
|
<div class="inline-flex items-center justify-center w-16 h-16 bg-red-100 rounded-full mb-4">
|
|
<svg class="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-lg font-medium text-white mb-2">Connection Failed</h3>
|
|
<p class="text-gray-400 mb-4">Unable to connect to VM console</p>
|
|
<button onclick="location.reload()"
|
|
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
|
|
Try Again
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`);
|
|
})
|
|
|
|
// Control Functions
|
|
window.enterFullScreen = function() {
|
|
if (wmks) {
|
|
wmks.enterFullScreen();
|
|
}
|
|
};
|
|
|
|
window.sendCAD = function() {
|
|
if (wmks) {
|
|
wmks.sendCAD();
|
|
}
|
|
};
|
|
|
|
window.sendInputString = function() {
|
|
const text = document.getElementById('sendStringText').value;
|
|
if (text && wmks) {
|
|
wmks.sendInputString(text);
|
|
document.getElementById('sendStringText').value = '';
|
|
}
|
|
};
|
|
|
|
// Keyboard shortcuts
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'F11') {
|
|
e.preventDefault();
|
|
enterFullScreen();
|
|
}
|
|
|
|
if (e.ctrlKey && e.key === 'Enter') {
|
|
sendInputString();
|
|
}
|
|
});
|
|
|
|
// Enter key for text input
|
|
document.getElementById('sendStringText').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
sendInputString();
|
|
}
|
|
});
|
|
</script>
|
|
@endsection
|