dangthr commited on
Commit
826bca0
·
verified ·
1 Parent(s): 1e9bb1e

remote_uploader.py

Browse files
Files changed (1) hide show
  1. websocket_uploader.py +210 -204
websocket_uploader.py CHANGED
@@ -1,227 +1,233 @@
1
  import asyncio
2
  import websockets
 
3
  import json
4
  import logging
5
  import sys
 
6
  import base64
7
  import os
8
- import time
9
- import ssl
10
  from urllib.parse import urlparse
11
 
12
- # 配置日志
13
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
  logger = logging.getLogger(__name__)
15
 
16
- def build_websocket_url(website_url):
17
- """构建WebSocket URL"""
18
- try:
19
- # 解析原始URL
20
- parsed = urlparse(website_url)
21
-
22
- # 构建WebSocket URL
23
- if parsed.scheme == 'https':
24
- ws_scheme = 'wss'
25
- else:
26
- ws_scheme = 'ws'
27
-
28
- # 提取主机名,移除端口号
29
- hostname = parsed.hostname
30
-
31
- # 构建WebSocket URL,使用端口8765
32
- ws_url = f"{ws_scheme}://{hostname}:8765/ws"
33
-
34
- logger.info(f"原始URL: {website_url}")
35
- logger.info(f"WebSocket URL: {ws_url}")
36
-
37
- return ws_url
38
-
39
- except Exception as e:
40
- logger.error(f"构建WebSocket URL时出错: {e}")
41
- # 回退方案:简单替换
42
- if website_url.startswith('https://'):
43
- return website_url.replace('https://', 'wss://').replace(':5001', ':8765') + '/ws'
44
- else:
45
- return website_url.replace('http://', 'ws://').replace(':5001', ':8765') + '/ws'
46
 
47
- async def upload_files_via_websocket(space_id, user_token, website_url):
48
- """通过WebSocket上传output目录中的所有文件"""
49
-
50
- output_dir = "output"
51
- if not os.path.exists(output_dir):
52
- logger.error(f"输出目录不存在: {output_dir}")
53
- return False
54
-
55
- # 获取所有文件
56
- files_to_upload = []
57
- for root, dirs, files in os.walk(output_dir):
58
- for file in files:
59
- file_path = os.path.join(root, file)
60
- if os.path.isfile(file_path):
61
- files_to_upload.append(file_path)
62
-
63
- if not files_to_upload:
64
- logger.info("输出目录中没有找到任何文件")
65
- return True
66
-
67
- logger.info(f"找到 {len(files_to_upload)} 个文件需要上传")
68
-
69
- # 构建WebSocket URL
70
- ws_url = build_websocket_url(website_url)
71
-
72
  try:
73
- # 连接到WebSocket服务器
74
- logger.info(f"正在连接到 WebSocket 服务器: {ws_url}")
75
-
76
- # 创建SSL上下文(如果是wss://)
77
- ssl_context = None
78
- if ws_url.startswith('wss://'):
79
- ssl_context = ssl.create_default_context()
80
- ssl_context.check_hostname = False
81
- ssl_context.verify_mode = ssl.CERT_NONE
82
-
83
- # 设置连接超时
84
- connect_timeout = 10
85
-
86
- async with websockets.connect(
87
- ws_url,
88
- ssl=ssl_context,
89
- ping_interval=20,
90
- ping_timeout=10,
91
- close_timeout=10
92
- ) as websocket:
93
- logger.info("WebSocket 连接成功")
94
-
95
- # 发送注册消息
96
- register_msg = {
97
- 'type': 'register',
98
- 'space_id': space_id,
99
- 'user_token': user_token
100
- }
101
- await websocket.send(json.dumps(register_msg))
102
- logger.info("已发送注册消息")
103
-
104
- # 等待注册确认
105
- try:
106
- response = await asyncio.wait_for(websocket.recv(), timeout=10)
107
- response_data = json.loads(response)
108
- logger.info(f"收到响应: {response_data}")
109
- except asyncio.TimeoutError:
110
- logger.error("等待注册确认超时")
111
- return False
112
-
113
- if response_data.get('type') == 'registered':
114
- logger.info("注册成功,开始上传文件")
115
-
116
- # 上传文件
117
- success_count = 0
118
- failed_count = 0
119
-
120
- for file_path in files_to_upload:
121
  try:
122
- filename = os.path.basename(file_path)
123
- logger.info(f"正在上传文件: {filename}")
124
-
125
- # 读取文件内容并编码为base64
126
- with open(file_path, 'rb') as f:
127
  file_content = f.read()
128
- file_b64 = base64.b64encode(file_content).decode('utf-8')
129
-
130
- file_size = len(file_content)
131
- logger.info(f"文件 {filename} 大小: {file_size} 字节")
132
-
133
- # 发送文件上传消息
134
- upload_msg = {
135
- 'type': 'file_upload',
136
- 'space_id': space_id,
137
- 'user_token': user_token,
138
- 'filename': filename,
139
- 'content': file_b64
140
  }
141
-
142
- await websocket.send(json.dumps(upload_msg))
143
- logger.info(f"已发送文件 {filename}")
144
-
145
- # 等待上传结果
146
- try:
147
- upload_response = await asyncio.wait_for(websocket.recv(), timeout=30)
148
- upload_result = json.loads(upload_response)
149
- logger.info(f"上传响应: {upload_result}")
150
- except asyncio.TimeoutError:
151
- logger.error(f"等待文件 {filename} 上传结果超时")
152
- failed_count += 1
153
- continue
154
-
155
- if upload_result.get('type') == 'upload_success':
156
- logger.info(f"文件 {filename} 上传成功")
157
- success_count += 1
158
- else:
159
- logger.error(f"文件 {filename} 上传失败: {upload_result}")
160
- failed_count += 1
161
-
162
- # 稍微延迟避免服务器压力过大
163
- await asyncio.sleep(0.5)
164
-
165
  except Exception as e:
166
- logger.error(f"上传文件 {file_path} 时发生错误: {e}")
167
- failed_count += 1
168
-
169
- logger.info(f"上传完成! 成功: {success_count}, 失败: {failed_count}")
170
-
171
- # 发送完成消息
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  try:
173
- complete_msg = {'type': 'upload_complete'}
174
- await websocket.send(json.dumps(complete_msg))
175
- logger.info("已发送完成消息")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  except Exception as e:
177
- logger.error(f"发送完成消息时出错: {e}")
178
-
179
- return success_count > 0
180
-
181
- elif response_data.get('type') == 'error':
182
- logger.error(f"注册失败: {response_data.get('message', '未知错误')}")
183
- return False
184
- else:
185
- logger.error(f"未知的响应类型: {response_data}")
186
- return False
187
-
188
- except websockets.exceptions.InvalidURI as e:
189
- logger.error(f"无效的WebSocket URI: {e}")
190
- return False
191
- except websockets.exceptions.ConnectionClosed as e:
192
- logger.error(f"WebSocket连接被关闭: {e}")
193
- return False
194
  except Exception as e:
195
- logger.error(f"WebSocket 连接或上传过程中发生错误: {e}")
196
- return False
197
 
198
- def main():
199
- if len(sys.argv) != 4:
200
- print("用法: python3 websocket_uploader.py <space_id> <user_token> <website_url>")
201
- sys.exit(1)
202
-
203
- space_id = sys.argv[1]
204
- user_token = sys.argv[2]
205
- website_url = sys.argv[3]
206
-
207
- logger.info(f"开始 WebSocket 文件上传")
208
- logger.info(f"Space ID: {space_id}")
209
- logger.info(f"User Token: {user_token[:8]}...")
210
- logger.info(f"Website URL: {website_url}")
211
-
212
- # 运行异步上传
213
- try:
214
- result = asyncio.run(upload_files_via_websocket(space_id, user_token, website_url))
215
-
216
- if result:
217
- logger.info("所有文件上传成功")
218
- sys.exit(0)
219
- else:
220
- logger.error("文件上传失败")
221
- sys.exit(1)
222
- except Exception as e:
223
- logger.error(f"运行上传程序时出错: {e}")
224
- sys.exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
  if __name__ == "__main__":
227
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
  import websockets
3
+ import subprocess
4
  import json
5
  import logging
6
  import sys
7
+ import urllib.parse
8
  import base64
9
  import os
10
+ import threading
11
+ import requests
12
  from urllib.parse import urlparse
13
 
14
+ # Configure logging
15
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
16
  logger = logging.getLogger(__name__)
17
 
18
+ # 全局变量,用于在用户输入时访问 WebSocket
19
+ global_websocket = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ async def connect(space_id, machine_secret, token, upload_file=None, upload_dir=None):
22
+ global global_websocket
23
+ # 使用查询参数传递 machine_secret 和 token
24
+ encoded_secret = urllib.parse.quote(machine_secret)
25
+ encoded_token = urllib.parse.quote(token)
26
+ uri = f"wss://remote-terminal-worker.nianxi4563.workers.dev/terminal/{space_id}?secret={encoded_secret}&token={encoded_token}"
27
+ logger.info(f"Attempting to connect to {uri}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  try:
29
+ async with websockets.connect(uri, ping_interval=20, ping_timeout=60) as websocket:
30
+ global_websocket = websocket
31
+ logger.info("Connected to WebSocket")
32
+ machine_info = {"type": "machine", "space_id": space_id, "token": token}
33
+ await websocket.send(json.dumps(machine_info))
34
+ logger.debug(f"Sent machine registration: {machine_info}")
35
+
36
+ # 如果指定了上传文件,发送文件内容
37
+ if upload_file:
38
+ if os.path.exists(upload_file):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  try:
40
+ with open(upload_file, 'rb') as f:
 
 
 
 
41
  file_content = f.read()
42
+ file_b64 = base64.b64encode(file_content).decode('utf-8')
43
+ file_msg = {
44
+ "type": "file_upload",
45
+ "filename": os.path.basename(upload_file),
46
+ "content": file_b64,
47
+ "token": token
 
 
 
 
 
 
48
  }
49
+ await websocket.send(json.dumps(file_msg))
50
+ logger.info(f"Uploaded file: {upload_file}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  except Exception as e:
52
+ logger.error(f"Error uploading file {upload_file}: {e}")
53
+ else:
54
+ logger.error(f"File not found: {upload_file}")
55
+
56
+ # 如果指定了上传目录,扫描并上传所有文件
57
+ if upload_dir:
58
+ if os.path.exists(upload_dir):
59
+ all_files = []
60
+ for root, dirs, files in os.walk(upload_dir):
61
+ for file in files:
62
+ file_path = os.path.join(root, file)
63
+ if os.path.isfile(file_path):
64
+ all_files.append(file_path)
65
+ if all_files:
66
+ logger.info(f"Found {len(all_files)} files in {upload_dir}, starting upload...")
67
+ for file_path in all_files:
68
+ try:
69
+ with open(file_path, 'rb') as f:
70
+ file_content = f.read()
71
+ file_b64 = base64.b64encode(file_content).decode('utf-8')
72
+ file_msg = {
73
+ "type": "file_upload",
74
+ "filename": os.path.basename(file_path),
75
+ "content": file_b64,
76
+ "token": token
77
+ }
78
+ await websocket.send(json.dumps(file_msg))
79
+ logger.info(f"Uploaded file from dir: {file_path}")
80
+ except Exception as e:
81
+ logger.error(f"Error uploading file {file_path}: {e}")
82
+ else:
83
+ logger.info(f"No files found in {upload_dir}")
84
+ else:
85
+ logger.error(f"Directory not found: {upload_dir}")
86
+
87
+ # 创建下载目录
88
+ os.makedirs("./downloads", exist_ok=True)
89
+
90
+ # 启动用户输入监听(非阻塞)
91
+ asyncio.create_task(listen_user_input())
92
+
93
+ while True:
94
  try:
95
+ message = await websocket.recv()
96
+ logger.debug(f"Received message: {message}")
97
+ data = json.loads(message)
98
+ if data["type"] == "command":
99
+ command = data["command"]
100
+ logger.info(f"Executing command: {command}")
101
+ try:
102
+ process = subprocess.run(command, shell=True, capture_output=True, text=True)
103
+ output = process.stdout + process.stderr
104
+ logger.debug(f"Command output: {output}")
105
+ if process.returncode == 0:
106
+ await websocket.send(json.dumps({"type": "output", "data": output}))
107
+ else:
108
+ await websocket.send(json.dumps({"type": "error", "data": output}))
109
+ except Exception as e:
110
+ error_message = f"Error executing command: {e}"
111
+ logger.error(error_message)
112
+ await websocket.send(json.dumps({"type": "error", "data": error_message}))
113
+ elif data["type"] == "file_download_url":
114
+ # 处理从服务器发来的文件下载URL
115
+ url = data["url"]
116
+ filename = data["filename"]
117
+ logger.info(f"Received file download URL: {url}")
118
+ try:
119
+ download_dir = "./downloads"
120
+ os.makedirs(download_dir, exist_ok=True)
121
+ response = requests.get(url)
122
+ if response.status_code == 200:
123
+ file_path = os.path.join(download_dir, filename)
124
+ with open(file_path, 'wb') as f:
125
+ f.write(response.content)
126
+ success_msg = f"File downloaded successfully: {file_path}"
127
+ logger.info(success_msg)
128
+ await websocket.send(json.dumps({
129
+ "type": "output",
130
+ "data": success_msg
131
+ }))
132
+ else:
133
+ error_msg = f"Failed to download file. Status code: {response.status_code}"
134
+ logger.error(error_msg)
135
+ await websocket.send(json.dumps({
136
+ "type": "error",
137
+ "data": error_msg
138
+ }))
139
+ except Exception as e:
140
+ error_msg = f"Error downloading file from URL: {e}"
141
+ logger.error(error_msg)
142
+ await websocket.send(json.dumps({
143
+ "type": "error",
144
+ "data": error_msg
145
+ }))
146
+ elif data["type"] == "ping":
147
+ logger.debug("Received ping, sending pong")
148
+ await websocket.send(json.dumps({"type": "pong"}))
149
+ except websockets.exceptions.ConnectionClosed:
150
+ logger.error("WebSocket connection closed")
151
+ break
152
  except Exception as e:
153
+ logger.error(f"Error processing message: {e}")
154
+ await websocket.send(json.dumps({"type": "error", "data": str(e)}))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  except Exception as e:
156
+ logger.error(f"Failed to connect or maintain connection: {e}")
 
157
 
158
+ async def listen_user_input():
159
+ global global_websocket
160
+ logger.info("Started listening for user input. Use ' --upload' to upload files to server.")
161
+ while True:
162
+ try:
163
+ user_input = await asyncio.to_thread(input, "Enter command or file path to upload: ")
164
+ if user_input.strip():
165
+ parts = user_input.strip().split()
166
+ if len(parts) >= 2 and parts[-1] == "--upload":
167
+ filename = " ".join(parts[:-1])
168
+ if os.path.exists(filename):
169
+ try:
170
+ with open(filename, 'rb') as f:
171
+ file_content = f.read()
172
+ file_b64 = base64.b64encode(file_content).decode('utf-8')
173
+ file_msg = {
174
+ "type": "file_upload",
175
+ "filename": os.path.basename(filename),
176
+ "content": file_b64
177
+ }
178
+ if global_websocket:
179
+ await global_websocket.send(json.dumps(file_msg))
180
+ logger.info(f"Uploaded file via input: {filename}")
181
+ else:
182
+ logger.error("WebSocket not connected")
183
+ except Exception as e:
184
+ logger.error(f"Error uploading file {filename}: {e}")
185
+ else:
186
+ logger.error(f"File not found: {filename}")
187
+ elif len(parts) >= 2 and parts[-1] == "--download":
188
+ url = " ".join(parts[:-1])
189
+ try:
190
+ parsed_url = urlparse(url)
191
+ filename = os.path.basename(parsed_url.path)
192
+ download_dir = "./downloads"
193
+ os.makedirs(download_dir, exist_ok=True)
194
+ logger.info(f"Downloading file from URL: {url}")
195
+ response = requests.get(url)
196
+ if response.status_code == 200:
197
+ file_path = os.path.join(download_dir, filename)
198
+ with open(file_path, 'wb') as f:
199
+ f.write(response.content)
200
+ logger.info(f"File downloaded successfully: {file_path}")
201
+ else:
202
+ logger.error(f"Failed to download file. Status code: {response.status_code}")
203
+ except Exception as e:
204
+ logger.error(f"Error downloading from URL {url}: {e}")
205
+ else:
206
+ if global_websocket:
207
+ await global_websocket.send(json.dumps({
208
+ "type": "command",
209
+ "command": user_input
210
+ }))
211
+ logger.info(f"Sent command: {user_input}")
212
+ else:
213
+ logger.error("WebSocket not connected")
214
+ await asyncio.sleep(0.1)
215
+ except Exception as e:
216
+ logger.error(f"Error in user input listener: {e}")
217
+ await asyncio.sleep(1)
218
 
219
  if __name__ == "__main__":
220
+ if len(sys.argv) < 4:
221
+ print("Usage: python remote_client.py <space_id> <machine_secret> <token> [--upload <file_path>] [--upload-dir <dir_path>]")
222
+ sys.exit(1)
223
+ space_id = sys.argv[1]
224
+ machine_secret = sys.argv[2]
225
+ token = sys.argv[3]
226
+ upload_file = None
227
+ upload_dir = None
228
+ if len(sys.argv) > 4:
229
+ if sys.argv[4] == "--upload" and len(sys.argv) > 5:
230
+ upload_file = sys.argv[5]
231
+ elif sys.argv[4] == "--upload-dir" and len(sys.argv) > 5:
232
+ upload_dir = sys.argv[5]
233
+ asyncio.run(connect(space_id, machine_secret, token, upload_file, upload_dir))