Spaces:
Sleeping
Sleeping
Upload 5 files
#4
by e2dew32 - opened
app.py
CHANGED
|
@@ -137,6 +137,7 @@ HTML_CONTENT = '''<!DOCTYPE html>
|
|
| 137 |
<div class="tabs">
|
| 138 |
<button class="tab active" data-tab="shell">Shell</button>
|
| 139 |
<button class="tab" data-tab="screenshot">截图</button>
|
|
|
|
| 140 |
<button class="tab" data-tab="control">远程控制</button>
|
| 141 |
</div>
|
| 142 |
<div class="tab-content" id="tab-shell">
|
|
@@ -166,9 +167,19 @@ HTML_CONTENT = '''<!DOCTYPE html>
|
|
| 166 |
<label style="color:#00d9ff;">
|
| 167 |
<input type="radio" name="clickButton" value="right"> 右键
|
| 168 |
</label>
|
| 169 |
-
</div>
|
| 170 |
</div>
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
</div>
|
| 173 |
<div class="tab-content" id="tab-control" style="display:none;">
|
| 174 |
<p style="color:#888;margin-bottom:15px;">远程控制</p>
|
|
@@ -372,6 +383,37 @@ HTML_CONTENT = '''<!DOCTYPE html>
|
|
| 372 |
});
|
| 373 |
}
|
| 374 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
document.querySelectorAll('.tab').forEach(tab => {
|
| 376 |
tab.onclick = () => {
|
| 377 |
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
@@ -485,6 +527,35 @@ async def get_screenshot(client_id: str):
|
|
| 485 |
return FileResponse(files[0], media_type='image/jpeg')
|
| 486 |
raise HTTPException(status_code=404, detail="No screenshot")
|
| 487 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 488 |
@app.get("/api/clients")
|
| 489 |
async def get_clients():
|
| 490 |
conn = get_db()
|
|
|
|
| 137 |
<div class="tabs">
|
| 138 |
<button class="tab active" data-tab="shell">Shell</button>
|
| 139 |
<button class="tab" data-tab="screenshot">截图</button>
|
| 140 |
+
<button class="tab" data-tab="files">文件</button>
|
| 141 |
<button class="tab" data-tab="control">远程控制</button>
|
| 142 |
</div>
|
| 143 |
<div class="tab-content" id="tab-shell">
|
|
|
|
| 167 |
<label style="color:#00d9ff;">
|
| 168 |
<input type="radio" name="clickButton" value="right"> 右键
|
| 169 |
</label>
|
|
|
|
| 170 |
</div>
|
| 171 |
+
</div>
|
| 172 |
+
<div class="tab-content" id="tab-files" style="display:none;">
|
| 173 |
+
<div class="command-input" style="margin-bottom:15px;">
|
| 174 |
+
<input type="text" id="filePath" placeholder="客户端文件路径,如 C:\\test.txt" style="flex:1;">
|
| 175 |
+
<button class="btn" onclick="downloadClientFile()">下载到服务器</button>
|
| 176 |
+
</div>
|
| 177 |
+
<div class="command-input" style="margin-bottom:15px;">
|
| 178 |
+
<input type="file" id="uploadFile">
|
| 179 |
+
<input type="text" id="uploadPath" placeholder="目标路径,如 C:\\uploaded.exe" style="flex:1;">
|
| 180 |
+
<button class="btn" onclick="uploadToClient()">上传到客户端</button>
|
| 181 |
+
</div>
|
| 182 |
+
<div class="output" id="fileOutput">文件操作结果...</div>
|
| 183 |
</div>
|
| 184 |
<div class="tab-content" id="tab-control" style="display:none;">
|
| 185 |
<p style="color:#888;margin-bottom:15px;">远程控制</p>
|
|
|
|
| 383 |
});
|
| 384 |
}
|
| 385 |
|
| 386 |
+
async function downloadClientFile() {
|
| 387 |
+
const path = document.getElementById('filePath').value.trim();
|
| 388 |
+
if (!path || !selectedClient) return;
|
| 389 |
+
document.getElementById('fileOutput').textContent = '发送下载命令...';
|
| 390 |
+
await fetch('/api/command', {
|
| 391 |
+
method: 'POST', headers: {'Content-Type': 'application/json'},
|
| 392 |
+
body: JSON.stringify({client_id: selectedClient.id, type: 'file_download', payload: path})
|
| 393 |
+
});
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
async function uploadToClient() {
|
| 397 |
+
const fileInput = document.getElementById('uploadFile');
|
| 398 |
+
const path = document.getElementById('uploadPath').value.trim();
|
| 399 |
+
if (!fileInput.files[0] || !path || !selectedClient) {
|
| 400 |
+
document.getElementById('fileOutput').textContent = '请选择文件和目标路径';
|
| 401 |
+
return;
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
document.getElementById('fileOutput').textContent = '上传文件中...';
|
| 405 |
+
const formData = new FormData();
|
| 406 |
+
formData.append('file', fileInput.files[0]);
|
| 407 |
+
formData.append('client_id', selectedClient.id);
|
| 408 |
+
formData.append('path', path);
|
| 409 |
+
|
| 410 |
+
await fetch('/api/file/upload_to_client', {
|
| 411 |
+
method: 'POST',
|
| 412 |
+
body: formData
|
| 413 |
+
});
|
| 414 |
+
document.getElementById('fileOutput').textContent = '文件已上传到服务端';
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
document.querySelectorAll('.tab').forEach(tab => {
|
| 418 |
tab.onclick = () => {
|
| 419 |
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
|
|
| 527 |
return FileResponse(files[0], media_type='image/jpeg')
|
| 528 |
raise HTTPException(status_code=404, detail="No screenshot")
|
| 529 |
|
| 530 |
+
@app.post("/api/file/upload_to_client")
|
| 531 |
+
async def upload_to_client(client_id: str = Form(...), path: str = Form(...), file: UploadFile = File(...)):
|
| 532 |
+
filename = f'{client_id}_{int(datetime.now().timestamp())}_{file.filename}'
|
| 533 |
+
filepath = f'{UPLOAD_DIR}/files/{filename}'
|
| 534 |
+
|
| 535 |
+
content = await file.read()
|
| 536 |
+
async with aiofiles.open(filepath, 'wb') as f:
|
| 537 |
+
await f.write(content)
|
| 538 |
+
|
| 539 |
+
# 发送命令让客户端下载
|
| 540 |
+
cmd_id = str(uuid.uuid4())
|
| 541 |
+
conn = get_db()
|
| 542 |
+
conn.execute('''INSERT INTO commands (id, client_id, type, payload, status, created_at)
|
| 543 |
+
VALUES (?, ?, 'file_upload', ?, 'pending', ?)''',
|
| 544 |
+
(cmd_id, client_id, filepath, datetime.now().isoformat()))
|
| 545 |
+
conn.commit()
|
| 546 |
+
conn.close()
|
| 547 |
+
|
| 548 |
+
return {'status': 'ok', 'cmd_id': cmd_id, 'filepath': filepath}
|
| 549 |
+
|
| 550 |
+
@app.get("/api/file/download")
|
| 551 |
+
async def download_file_from_client(client_id: str = "", filename: str = ""):
|
| 552 |
+
if not client_id or not filename:
|
| 553 |
+
raise HTTPException(status_code=400, detail="client_id and filename required")
|
| 554 |
+
filepath = f'{UPLOAD_DIR}/files/{client_id}_{filename}'
|
| 555 |
+
if os.path.exists(filepath):
|
| 556 |
+
return FileResponse(filepath)
|
| 557 |
+
raise HTTPException(status_code=404, detail="File not found")
|
| 558 |
+
|
| 559 |
@app.get("/api/clients")
|
| 560 |
async def get_clients():
|
| 561 |
conn = get_db()
|