Выполнение команд, получение инфы о системе. Короче работает все кроме доступа к графической оболочке. Доступ к шеллу через веб
799 lines
28 KiB
Python
799 lines
28 KiB
Python
from flask import Flask, render_template, request, jsonify, session
|
||
import subprocess
|
||
import os
|
||
import uuid
|
||
import time
|
||
from datetime import datetime
|
||
import base64
|
||
import io
|
||
import platform
|
||
import socket
|
||
from PIL import Image
|
||
|
||
app = Flask(__name__)
|
||
app.secret_key = os.urandom(24)
|
||
|
||
|
||
class RemoteDesktop:
|
||
def __init__(self):
|
||
self.sessions = {}
|
||
self.screen_quality = 80
|
||
self.screen_scale = 0.7
|
||
self.screenshot_method = None
|
||
self._detect_screenshot_method()
|
||
|
||
def _detect_screenshot_method(self):
|
||
"""Автоматическое определение доступного метода захвата экрана"""
|
||
methods = []
|
||
|
||
# Проверяем системные утилиты для Ubuntu
|
||
if subprocess.run(['which', 'gnome-screenshot'], capture_output=True).returncode == 0:
|
||
methods.append('gnome-screenshot')
|
||
if subprocess.run(['which', 'scrot'], capture_output=True).returncode == 0:
|
||
methods.append('scrot')
|
||
if subprocess.run(['which', 'import'], capture_output=True).returncode == 0:
|
||
methods.append('imagemagick')
|
||
|
||
# Проверяем Python библиотеки
|
||
try:
|
||
import mss
|
||
methods.append('mss')
|
||
except ImportError:
|
||
pass
|
||
|
||
print(f"📸 Доступные методы захвата экрана: {methods}")
|
||
|
||
if methods:
|
||
self.screenshot_method = methods[0]
|
||
print(f"✅ Выбран метод: {self.screenshot_method}")
|
||
else:
|
||
print("❌ Не найдены методы захвата экрана")
|
||
|
||
def create_session(self):
|
||
session_id = str(uuid.uuid4())
|
||
self.sessions[session_id] = {
|
||
'created_at': datetime.now(),
|
||
'last_activity': datetime.now()
|
||
}
|
||
return session_id
|
||
|
||
def get_system_info(self):
|
||
"""Получение информации о системе"""
|
||
try:
|
||
# Получаем информацию о дистрибутиве Ubuntu
|
||
distro_info = ""
|
||
try:
|
||
with open('/etc/lsb-release', 'r') as f:
|
||
for line in f:
|
||
if line.startswith('DISTRIB_DESCRIPTION'):
|
||
distro_info = line.split('=')[1].strip().strip('"')
|
||
except:
|
||
distro_info = "Ubuntu (точная версия неизвестна)"
|
||
|
||
system_info = {
|
||
'distribution': distro_info,
|
||
'platform': platform.system(),
|
||
'platform_release': platform.release(),
|
||
'architecture': platform.machine(),
|
||
'hostname': socket.gethostname(),
|
||
'username': os.getenv('USER'),
|
||
'display': os.environ.get('DISPLAY', ':0'),
|
||
'screenshot_method': self.screenshot_method,
|
||
'python_version': platform.python_version(),
|
||
'current_directory': os.getcwd()
|
||
}
|
||
return system_info
|
||
except Exception as e:
|
||
return {'error': str(e)}
|
||
|
||
def capture_screenshot(self):
|
||
"""Создание скриншота рабочего стола"""
|
||
try:
|
||
if not self.screenshot_method:
|
||
return None, "Не найдены методы захвата экрана"
|
||
|
||
if self.screenshot_method == 'gnome-screenshot':
|
||
return self._capture_with_gnome_screenshot()
|
||
elif self.screenshot_method == 'scrot':
|
||
return self._capture_with_scrot()
|
||
elif self.screenshot_method == 'imagemagick':
|
||
return self._capture_with_imagemagick()
|
||
elif self.screenshot_method == 'mss':
|
||
return self._capture_with_mss()
|
||
else:
|
||
return None, f"Неизвестный метод: {self.screenshot_method}"
|
||
|
||
except Exception as e:
|
||
return None, f"Ошибка захвата экрана: {str(e)}"
|
||
|
||
def _capture_with_gnome_screenshot(self):
|
||
"""Захват экрана с помощью gnome-screenshot (рекомендуется для Ubuntu)"""
|
||
try:
|
||
# Используем временный файл для надежности
|
||
temp_file = f"/tmp/screenshot_{int(time.time())}.png"
|
||
|
||
result = subprocess.run([
|
||
'gnome-screenshot', '-f', temp_file, '--include-pointer'
|
||
], capture_output=True, text=True, timeout=10)
|
||
|
||
if result.returncode != 0:
|
||
return None, f"GNOME Screenshot failed: {result.stderr}"
|
||
|
||
# Читаем файл
|
||
with open(temp_file, 'rb') as f:
|
||
img_data = f.read()
|
||
|
||
# Удаляем временный файл
|
||
os.unlink(temp_file)
|
||
|
||
img = Image.open(io.BytesIO(img_data))
|
||
return self._encode_image(img), None
|
||
|
||
except subprocess.TimeoutExpired:
|
||
return None, "GNOME Screenshot timeout"
|
||
except Exception as e:
|
||
return None, f"GNOME Screenshot error: {str(e)}"
|
||
|
||
def _capture_with_scrot(self):
|
||
"""Захват экрана с помощью scrot"""
|
||
try:
|
||
temp_file = f"/tmp/screenshot_{int(time.time())}.png"
|
||
|
||
result = subprocess.run(['scrot', '-o', temp_file],
|
||
capture_output=True, text=True, timeout=10)
|
||
|
||
if result.returncode != 0:
|
||
return None, f"Scrot failed: {result.stderr}"
|
||
|
||
with open(temp_file, 'rb') as f:
|
||
img_data = f.read()
|
||
os.unlink(temp_file)
|
||
|
||
img = Image.open(io.BytesIO(img_data))
|
||
return self._encode_image(img), None
|
||
|
||
except subprocess.TimeoutExpired:
|
||
return None, "Scrot timeout"
|
||
except Exception as e:
|
||
return None, f"Scrot error: {str(e)}"
|
||
|
||
def _capture_with_imagemagick(self):
|
||
"""Захват экрана с помощью ImageMagick"""
|
||
try:
|
||
result = subprocess.run(['import', '-window', 'root', 'png:-'],
|
||
capture_output=True, timeout=10)
|
||
|
||
if result.returncode != 0:
|
||
return None, f"ImageMagick failed: {result.stderr}"
|
||
|
||
img = Image.open(io.BytesIO(result.stdout))
|
||
return self._encode_image(img), None
|
||
except subprocess.TimeoutExpired:
|
||
return None, "ImageMagick timeout"
|
||
except Exception as e:
|
||
return None, f"ImageMagick error: {str(e)}"
|
||
|
||
def _capture_with_mss(self):
|
||
"""Захват экрана с помощью mss"""
|
||
try:
|
||
import mss
|
||
with mss.mss() as sct:
|
||
monitor = sct.monitors[1]
|
||
screenshot = sct.grab(monitor)
|
||
img = Image.frombytes("RGB", screenshot.size, screenshot.bgra, "raw", "BGRX")
|
||
return self._encode_image(img), None
|
||
except Exception as e:
|
||
return None, f"MSS error: {str(e)}"
|
||
|
||
def _encode_image(self, img):
|
||
"""Кодирование изображения в base64"""
|
||
try:
|
||
# Масштабирование
|
||
if self.screen_scale != 1.0:
|
||
new_size = (int(img.width * self.screen_scale),
|
||
int(img.height * self.screen_scale))
|
||
img = img.resize(new_size, Image.Resampling.LANCZOS)
|
||
|
||
buffer = io.BytesIO()
|
||
img.save(buffer, format='JPEG', quality=self.screen_quality)
|
||
return base64.b64encode(buffer.getvalue()).decode('utf-8')
|
||
except Exception as e:
|
||
raise Exception(f"Image encoding error: {str(e)}")
|
||
|
||
def execute_command(self, command):
|
||
"""Выполнение команды в оболочке"""
|
||
try:
|
||
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)
|
||
output = result.stdout
|
||
if result.stderr:
|
||
output += f"\nSTDERR: {result.stderr}"
|
||
return output, None
|
||
except subprocess.TimeoutExpired:
|
||
return None, "Command timeout"
|
||
except Exception as e:
|
||
return None, f"Command error: {str(e)}"
|
||
|
||
def send_key(self, key):
|
||
"""Отправка клавиши (эмуляция)"""
|
||
try:
|
||
# Используем xdotool для эмуляции клавиатуры
|
||
result = subprocess.run(['xdotool', 'key', key],
|
||
capture_output=True, text=True, timeout=10)
|
||
if result.returncode == 0:
|
||
return True, None
|
||
else:
|
||
return False, result.stderr
|
||
except Exception as e:
|
||
return False, f"Keyboard error: {str(e)}"
|
||
|
||
def mouse_click(self, x, y, button='left'):
|
||
"""Клик мыши в указанных координатах"""
|
||
try:
|
||
# Используем xdotool для эмуляции мыши
|
||
if button == 'left':
|
||
click_arg = '1'
|
||
elif button == 'right':
|
||
click_arg = '3'
|
||
else:
|
||
click_arg = '1'
|
||
|
||
result = subprocess.run([
|
||
'xdotool', 'mousemove', str(x), str(y), 'click', click_arg
|
||
], capture_output=True, text=True, timeout=10)
|
||
|
||
if result.returncode == 0:
|
||
return True, None
|
||
else:
|
||
return False, result.stderr
|
||
except Exception as e:
|
||
return False, f"Mouse click error: {str(e)}"
|
||
|
||
|
||
remote_desktop = RemoteDesktop()
|
||
|
||
|
||
@app.route('/')
|
||
def index():
|
||
if 'session_id' not in session:
|
||
session['session_id'] = remote_desktop.create_session()
|
||
|
||
system_info = remote_desktop.get_system_info()
|
||
return render_template('remote_desktop.html', system_info=system_info)
|
||
|
||
|
||
@app.route('/system-info')
|
||
def system_info():
|
||
info = remote_desktop.get_system_info()
|
||
return jsonify(info)
|
||
|
||
|
||
@app.route('/screenshot')
|
||
def screenshot():
|
||
"""Получение скриншота"""
|
||
if 'session_id' not in session:
|
||
return jsonify({'error': 'Сессия не найдена'})
|
||
|
||
image_data, error = remote_desktop.capture_screenshot()
|
||
if error:
|
||
return jsonify({'error': error})
|
||
|
||
return jsonify({'image': image_data})
|
||
|
||
|
||
@app.route('/command', methods=['POST'])
|
||
def execute_command():
|
||
"""Выполнение команды"""
|
||
if 'session_id' not in session:
|
||
return jsonify({'error': 'Сессия не найдена'})
|
||
|
||
data = request.get_json()
|
||
command = data.get('command', '').strip()
|
||
|
||
if not command:
|
||
return jsonify({'error': 'Пустая команда'})
|
||
|
||
output, error = remote_desktop.execute_command(command)
|
||
if error:
|
||
return jsonify({'error': error})
|
||
|
||
return jsonify({'output': output})
|
||
|
||
|
||
@app.route('/keyboard', methods=['POST'])
|
||
def keyboard_action():
|
||
"""Эмуляция клавиатуры"""
|
||
if 'session_id' not in session:
|
||
return jsonify({'error': 'Сессия не найдена'})
|
||
|
||
data = request.get_json()
|
||
key = data.get('key', '')
|
||
|
||
if not key:
|
||
return jsonify({'error': 'Не указана клавиша'})
|
||
|
||
success, error = remote_desktop.send_key(key)
|
||
if not success:
|
||
return jsonify({'error': error})
|
||
|
||
return jsonify({'status': 'success'})
|
||
|
||
|
||
@app.route('/mouse/click', methods=['POST'])
|
||
def mouse_click():
|
||
"""Клик мыши"""
|
||
if 'session_id' not in session:
|
||
return jsonify({'error': 'Сессия не найдена'})
|
||
|
||
data = request.get_json()
|
||
x = data.get('x', 0)
|
||
y = data.get('y', 0)
|
||
button = data.get('button', 'left')
|
||
|
||
success, error = remote_desktop.mouse_click(x, y, button)
|
||
if not success:
|
||
return jsonify({'error': error})
|
||
|
||
return jsonify({'status': 'success'})
|
||
|
||
|
||
@app.route('/settings', methods=['POST'])
|
||
def update_settings():
|
||
"""Обновление настроек"""
|
||
if 'session_id' not in session:
|
||
return jsonify({'error': 'Сессия не найдена'})
|
||
|
||
data = request.get_json()
|
||
quality = data.get('quality')
|
||
scale = data.get('scale')
|
||
|
||
if quality is not None:
|
||
remote_desktop.screen_quality = max(10, min(100, quality))
|
||
if scale is not None:
|
||
remote_desktop.screen_scale = max(0.1, min(1.0, scale))
|
||
|
||
return jsonify({
|
||
'quality': remote_desktop.screen_quality,
|
||
'scale': remote_desktop.screen_scale
|
||
})
|
||
|
||
|
||
@app.route('/test')
|
||
def test_page():
|
||
"""Тестовая страница"""
|
||
image_data, error = remote_desktop.capture_screenshot()
|
||
|
||
if error:
|
||
return f"""
|
||
<html>
|
||
<body style="background: #1e1e1e; color: white; font-family: Ubuntu, Arial; padding: 20px;">
|
||
<h2>❌ Ошибка захвата экрана</h2>
|
||
<p>{error}</p>
|
||
<h3>Решение для Ubuntu:</h3>
|
||
<code style="background: #2d2d30; padding: 15px; display: block;">
|
||
sudo apt update<br>
|
||
sudo apt install gnome-screenshot scrot imagemagick xdotool<br>
|
||
pip install mss pillow
|
||
</code>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
return f"""
|
||
<html>
|
||
<body style="background: #1e1e1e; color: white; font-family: Ubuntu, Arial; padding: 20px;">
|
||
<h2>✅ Тест захвата экрана - УСПЕШНО!</h2>
|
||
<p>Метод: {remote_desktop.screenshot_method}</p>
|
||
<img src="data:image/jpeg;base64,{image_data}" style="max-width: 800px; border: 2px solid #007acc; border-radius: 5px;">
|
||
<p><a href="/" style="color: #007acc; text-decoration: none;">← Вернуться к удаленному рабочему столу</a></p>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
|
||
if __name__ == '__main__':
|
||
os.makedirs('templates', exist_ok=True)
|
||
|
||
with open('templates/remote_desktop.html', 'w', encoding='utf-8') as f:
|
||
f.write('''<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Удаленный рабочий стол Ubuntu</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Ubuntu', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
background: #1e1e1e;
|
||
color: #ffffff;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.header {
|
||
background: #2d2d30;
|
||
padding: 20px;
|
||
border-bottom: 1px solid #3e3e42;
|
||
text-align: center;
|
||
}
|
||
|
||
.header h1 {
|
||
color: #007acc;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.controls {
|
||
display: flex;
|
||
gap: 10px;
|
||
background: #2d2d30;
|
||
padding: 15px;
|
||
border-bottom: 1px solid #3e3e42;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
}
|
||
|
||
button {
|
||
background: #007acc;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 20px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-family: 'Ubuntu', sans-serif;
|
||
transition: background 0.3s;
|
||
}
|
||
|
||
button:hover {
|
||
background: #005a9e;
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.desktop-container {
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 500px;
|
||
background: #000;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
#remote-screen {
|
||
max-width: 95%;
|
||
max-height: 75vh;
|
||
border: 3px solid #333;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
||
cursor: crosshair;
|
||
}
|
||
|
||
.status-bar {
|
||
background: #007acc;
|
||
padding: 10px 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 14px;
|
||
font-family: 'Ubuntu Mono', monospace;
|
||
}
|
||
|
||
.error-message {
|
||
background: #d32f2f;
|
||
color: white;
|
||
padding: 15px;
|
||
margin: 10px;
|
||
border-radius: 6px;
|
||
border-left: 4px solid #ff5252;
|
||
}
|
||
|
||
.info-panel {
|
||
background: #2d2d30;
|
||
padding: 20px;
|
||
margin: 15px;
|
||
border-radius: 8px;
|
||
border: 1px solid #3e3e42;
|
||
display: none;
|
||
}
|
||
|
||
.command-section {
|
||
background: #2d2d30;
|
||
padding: 20px;
|
||
margin: 15px;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.command-input {
|
||
width: 100%;
|
||
padding: 12px;
|
||
background: #1e1e1e;
|
||
border: 1px solid #3e3e42;
|
||
color: white;
|
||
border-radius: 6px;
|
||
font-family: 'Ubuntu Mono', monospace;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.placeholder {
|
||
color: #888;
|
||
font-size: 20px;
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
}
|
||
|
||
.success-badge {
|
||
background: #388e3c;
|
||
color: white;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
display: inline-block;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
#command-output {
|
||
background: #1e1e1e;
|
||
padding: 15px;
|
||
border-radius: 6px;
|
||
margin-top: 10px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
font-family: 'Ubuntu Mono', monospace;
|
||
font-size: 13px;
|
||
white-space: pre-wrap;
|
||
display: none;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<h1>🖥️ Удаленный рабочий стол Ubuntu</h1>
|
||
<p>Управление рабочим столом через веб-интерфейс</p>
|
||
</div>
|
||
|
||
<div class="controls">
|
||
<button onclick="refreshScreen()">🔄 Обновить экран</button>
|
||
<button onclick="toggleAutoRefresh()">⏱️ Авто-обновление</button>
|
||
<button onclick="showSystemInfo()">ℹ️ Информация о системе</button>
|
||
<button onclick="testConnection()">🧪 Тест подключения</button>
|
||
<button onclick="sendEnter()">↵ Отправить Enter</button>
|
||
<button onclick="sendCtrlC()">⎈ Ctrl+C</button>
|
||
</div>
|
||
|
||
<div id="error-container"></div>
|
||
<div id="info-panel" class="info-panel"></div>
|
||
|
||
<div class="desktop-container" id="desktop-container">
|
||
<div id="screen-placeholder" class="placeholder">
|
||
🖼️ Нажмите "Обновить экран" для захвата рабочего стола
|
||
</div>
|
||
<img id="remote-screen" style="display: none;" alt="Удаленный рабочий стол"
|
||
onclick="handleScreenClick(event)">
|
||
</div>
|
||
|
||
<div class="command-section">
|
||
<h3>💻 Командная строка</h3>
|
||
<input type="text" id="command-input" class="command-input"
|
||
placeholder="Введите команду (например: ls -la, pwd, gnome-terminal...)"
|
||
onkeypress="handleCommandInput(event)">
|
||
<button onclick="executeCommand()" style="width: 200px;">⚡ Выполнить команду</button>
|
||
<div id="command-output"></div>
|
||
</div>
|
||
|
||
<div class="status-bar">
|
||
<span id="status-info">Статус: Готов</span>
|
||
<span id="performance-info">Метод: {{ system_info.screenshot_method }} | Ubuntu</span>
|
||
</div>
|
||
|
||
<script>
|
||
let autoRefreshInterval = null;
|
||
let isAutoRefresh = false;
|
||
|
||
const remoteScreen = document.getElementById('remote-screen');
|
||
const screenPlaceholder = document.getElementById('screen-placeholder');
|
||
const statusInfo = document.getElementById('status-info');
|
||
const performanceInfo = document.getElementById('performance-info');
|
||
const infoPanel = document.getElementById('info-panel');
|
||
const errorContainer = document.getElementById('error-container');
|
||
const commandOutput = document.getElementById('command-output');
|
||
const commandInput = document.getElementById('command-input');
|
||
|
||
// Инициализация
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
updateStatus('Система готова к работе');
|
||
commandInput.focus();
|
||
});
|
||
|
||
function refreshScreen() {
|
||
updateStatus('Захват экрана...');
|
||
|
||
fetch('/screenshot?' + new Date().getTime())
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.error) {
|
||
showError('Ошибка: ' + data.error);
|
||
screenPlaceholder.style.display = 'block';
|
||
remoteScreen.style.display = 'none';
|
||
screenPlaceholder.innerHTML = '❌ ' + data.error;
|
||
} else if (data.image) {
|
||
hideError();
|
||
remoteScreen.src = 'data:image/jpeg;base64,' + data.image;
|
||
screenPlaceholder.style.display = 'none';
|
||
remoteScreen.style.display = 'block';
|
||
updateStatus('Экран обновлен ✓');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
showError('Ошибка подключения: ' + error);
|
||
});
|
||
}
|
||
|
||
function toggleAutoRefresh() {
|
||
isAutoRefresh = !isAutoRefresh;
|
||
if (isAutoRefresh) {
|
||
autoRefreshInterval = setInterval(refreshScreen, 3000);
|
||
updateStatus('Авто-обновление включено (3 сек)');
|
||
} else {
|
||
clearInterval(autoRefreshInterval);
|
||
updateStatus('Авто-обновление выключено');
|
||
}
|
||
}
|
||
|
||
function executeCommand() {
|
||
const command = commandInput.value.trim();
|
||
if (!command) return;
|
||
|
||
updateStatus('Выполнение команды...');
|
||
commandOutput.style.display = 'block';
|
||
commandOutput.textContent = 'Выполнение...';
|
||
|
||
fetch('/command', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ command: command })
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.error) {
|
||
commandOutput.textContent = 'Ошибка: ' + data.error;
|
||
commandOutput.style.color = '#ff5252';
|
||
} else {
|
||
commandOutput.textContent = data.output;
|
||
commandOutput.style.color = '#4caf50';
|
||
}
|
||
updateStatus('Команда выполнена ✓');
|
||
commandInput.value = '';
|
||
commandInput.focus();
|
||
})
|
||
.catch(error => {
|
||
commandOutput.textContent = 'Ошибка сети: ' + error;
|
||
commandOutput.style.color = '#ff5252';
|
||
updateStatus('Ошибка выполнения команды');
|
||
});
|
||
}
|
||
|
||
function handleCommandInput(event) {
|
||
if (event.key === 'Enter') {
|
||
executeCommand();
|
||
}
|
||
}
|
||
|
||
function handleScreenClick(event) {
|
||
const rect = remoteScreen.getBoundingClientRect();
|
||
const x = Math.round(event.clientX - rect.left);
|
||
const y = Math.round(event.clientY - rect.top);
|
||
|
||
// Масштабируем координаты для реального экрана
|
||
const scaleX = remoteScreen.naturalWidth / rect.width;
|
||
const scaleY = remoteScreen.naturalHeight / rect.height;
|
||
const realX = Math.round(x * scaleX);
|
||
const realY = Math.round(y * scaleY);
|
||
|
||
fetch('/mouse/click', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
x: realX,
|
||
y: realY,
|
||
button: event.button === 2 ? 'right' : 'left'
|
||
})
|
||
}).then(() => {
|
||
updateStatus('Клик мыши отправлен ✓');
|
||
setTimeout(refreshScreen, 500); // Обновляем экран после клика
|
||
}).catch(error => {
|
||
showError('Ошибка мыши: ' + error);
|
||
});
|
||
|
||
event.preventDefault();
|
||
}
|
||
|
||
function sendEnter() {
|
||
fetch('/keyboard', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ key: 'Return' })
|
||
}).then(() => {
|
||
updateStatus('Enter отправлен ✓');
|
||
setTimeout(refreshScreen, 500);
|
||
}).catch(error => {
|
||
showError('Ошибка клавиатуры: ' + error);
|
||
});
|
||
}
|
||
|
||
function sendCtrlC() {
|
||
fetch('/keyboard', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ key: 'ctrl+c' })
|
||
}).then(() => {
|
||
updateStatus('Ctrl+C отправлен ✓');
|
||
setTimeout(refreshScreen, 500);
|
||
}).catch(error => {
|
||
showError('Ошибка клавиатуры: ' + error);
|
||
});
|
||
}
|
||
|
||
function showSystemInfo() {
|
||
fetch('/system-info')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
let infoHtml = '<h3>🖥️ Информация о системе Ubuntu:</h3>';
|
||
for (const [key, value] of Object.entries(data)) {
|
||
infoHtml += `<p><strong>${key}:</strong> ${value}</p>`;
|
||
}
|
||
infoPanel.innerHTML = infoHtml;
|
||
infoPanel.style.display = 'block';
|
||
})
|
||
.catch(error => {
|
||
showError('Ошибка загрузки информации: ' + error);
|
||
});
|
||
}
|
||
|
||
function testConnection() {
|
||
window.open('/test', '_blank');
|
||
}
|
||
|
||
function updateStatus(message) {
|
||
statusInfo.textContent = 'Статус: ' + message;
|
||
}
|
||
|
||
function showError(message) {
|
||
errorContainer.innerHTML = `<div class="error-message">${message}</div>`;
|
||
updateStatus('Ошибка');
|
||
}
|
||
|
||
function hideError() {
|
||
errorContainer.innerHTML = '';
|
||
}
|
||
|
||
// Запрещаем контекстное меню на изображении
|
||
remoteScreen.addEventListener('contextmenu', function(e) {
|
||
e.preventDefault();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>''')
|
||
|
||
print("=" * 60)
|
||
print("🖥️ УДАЛЕННЫЙ РАБОЧИЙ СТОЛ ДЛЯ UBUNTU")
|
||
print("=" * 60)
|
||
|
||
system_info = remote_desktop.get_system_info()
|
||
print(f"💻 Система: {system_info.get('distribution', 'Ubuntu')}")
|
||
print(f"📸 Метод захвата: {remote_desktop.screenshot_method}")
|
||
|
||
if remote_desktop.screenshot_method:
|
||
print("✅ Захват экрана настроен корректно")
|
||
else:
|
||
print("❌ Установите зависимости:")
|
||
print(" sudo apt install gnome-screenshot scrot imagemagick xdotool")
|
||
|
||
print("🌐 Сервер запущен: http://0.0.0.0:5000")
|
||
print("🧪 Тест: http://0.0.0.0:5000/test")
|
||
print("=" * 60)
|
||
|
||
app.run(host='0.0.0.0', port=5000, debug=False, threaded=True) |