jyujiaf commited on
Commit
c87fa3b
·
verified ·
1 Parent(s): e8c0934

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -15
app.py CHANGED
@@ -42,33 +42,47 @@ HTML_CONTENT = """
42
  const wsUrl = `${protocol}//${window.location.host}/ws`;
43
 
44
  let sock = null;
 
45
 
46
  function connect() {
47
- document.getElementById('status').innerText = "Connecting to shell...";
 
 
48
  try {
49
  sock = new WebSocket(wsUrl);
50
 
51
  sock.onopen = () => {
52
  document.getElementById('status').innerText = "Connected (Ready)";
 
53
  term.write('\\r\\n\\x1b[32m>>> HF Space Ubuntu Terminal Ready <<<\\x1b[0m\\r\\n');
54
  term.write('\\r\\nType commands (e.g., ls, g++, vim)...\\r\\n\\r\\n');
55
  };
56
 
57
  term.onData(data => {
58
  if (sock && sock.readyState === WebSocket.OPEN) {
 
59
  sock.send(data);
60
  }
61
  });
62
 
63
- sock.onmessage = msg => term.write(msg.data);
 
 
 
 
 
 
 
64
 
65
  sock.onclose = () => {
66
  document.getElementById('status').innerText = "Disconnected. Reconnecting...";
67
- setTimeout(connect, 2000);
 
68
  };
69
 
70
  sock.onerror = () => {
71
  document.getElementById('status').innerText = "Connection Error.";
 
72
  };
73
  } catch (e) {
74
  document.getElementById('status').innerText = "Error: " + e.message;
@@ -86,12 +100,16 @@ async def handle_http(request):
86
  return web.Response(text=HTML_CONTENT, content_type='text/html')
87
 
88
  async def handle_ws(request):
89
- ws = web.WebSocketResponse()
90
  await ws.prepare(request)
91
 
92
- # 连接到本地运行的 ttyd
 
 
93
  try:
 
94
  reader, writer = await asyncio.open_connection('127.0.0.1', TTYD_PORT)
 
95
  except Exception as e:
96
  print(f"Failed to connect to ttyd: {e}")
97
  await ws.close()
@@ -103,34 +121,57 @@ async def handle_ws(request):
103
  if msg.type == web.WSMsgType.BINARY:
104
  writer.write(msg.data)
105
  elif msg.type == web.WSMsgType.TEXT:
106
- writer.write(msg.data.encode())
 
107
  await writer.drain()
108
- except Exception:
109
- pass
110
  finally:
111
- writer.close()
 
 
 
 
 
112
 
113
  async def tty_to_ws():
114
  try:
115
  while True:
 
116
  data = await reader.read(4096)
117
- if not data: # 【修复点】这里补全了判断条件
118
  break
 
119
  await ws.send_bytes(data)
120
- except Exception:
121
- pass
122
  finally:
123
- writer.close()
 
 
 
 
 
124
 
 
125
  await asyncio.gather(ws_to_tty(), tty_to_ws())
 
 
 
 
126
  return ws
127
 
128
  async def on_startup(app):
129
  # 启动 ttyd 子进程
 
 
130
  cmd = ["/usr/local/bin/ttyd", "-p", str(TTYD_PORT), "/bin/bash"]
131
  print(f"Starting ttyd: {' '.join(cmd)}")
132
- subprocess.Popen(cmd)
133
- print("ttyd started.")
 
 
 
134
 
135
  def main():
136
  app = web.Application()
@@ -139,6 +180,7 @@ def main():
139
  app.on_startup.append(on_startup)
140
 
141
  print(f"Starting server on port {SERVER_PORT}...")
 
142
  web.run_app(app, host='0.0.0.0', port=SERVER_PORT, print=lambda x: None)
143
 
144
  if __name__ == '__main__':
 
42
  const wsUrl = `${protocol}//${window.location.host}/ws`;
43
 
44
  let sock = null;
45
+ let reconnectTimer = null;
46
 
47
  function connect() {
48
+ if (reconnectTimer) clearTimeout(reconnectTimer);
49
+ document.getElementById('status').innerText = "Connecting...";
50
+
51
  try {
52
  sock = new WebSocket(wsUrl);
53
 
54
  sock.onopen = () => {
55
  document.getElementById('status').innerText = "Connected (Ready)";
56
+ document.getElementById('status').style.color = "#0f0";
57
  term.write('\\r\\n\\x1b[32m>>> HF Space Ubuntu Terminal Ready <<<\\x1b[0m\\r\\n');
58
  term.write('\\r\\nType commands (e.g., ls, g++, vim)...\\r\\n\\r\\n');
59
  };
60
 
61
  term.onData(data => {
62
  if (sock && sock.readyState === WebSocket.OPEN) {
63
+ // 直接发送字符串,aiohttp 会自动处理
64
  sock.send(data);
65
  }
66
  });
67
 
68
+ sock.onmessage = event => {
69
+ // 处理二进制或文本数据
70
+ if (event.data instanceof ArrayBuffer) {
71
+ term.write(new Uint8Array(event.data));
72
+ } else {
73
+ term.write(event.data);
74
+ }
75
+ };
76
 
77
  sock.onclose = () => {
78
  document.getElementById('status').innerText = "Disconnected. Reconnecting...";
79
+ document.getElementById('status').style.color = "#ffa500";
80
+ reconnectTimer = setTimeout(connect, 2000);
81
  };
82
 
83
  sock.onerror = () => {
84
  document.getElementById('status').innerText = "Connection Error.";
85
+ document.getElementById('status').style.color = "#ff0000";
86
  };
87
  } catch (e) {
88
  document.getElementById('status').innerText = "Error: " + e.message;
 
100
  return web.Response(text=HTML_CONTENT, content_type='text/html')
101
 
102
  async def handle_ws(request):
103
+ ws = web.WebSocketResponse(heartbeat=30) # 添加心跳防止超时断开
104
  await ws.prepare(request)
105
 
106
+ reader = None
107
+ writer = None
108
+
109
  try:
110
+ # 连接到本地运行的 ttyd
111
  reader, writer = await asyncio.open_connection('127.0.0.1', TTYD_PORT)
112
+ print("Connected to ttyd backend")
113
  except Exception as e:
114
  print(f"Failed to connect to ttyd: {e}")
115
  await ws.close()
 
121
  if msg.type == web.WSMsgType.BINARY:
122
  writer.write(msg.data)
123
  elif msg.type == web.WSMsgType.TEXT:
124
+ # ttyd 期望字节流,将文本编码为 bytes
125
+ writer.write(msg.data.encode('utf-8'))
126
  await writer.drain()
127
+ except Exception as e:
128
+ print(f"WS->TTY Error: {e}")
129
  finally:
130
+ if writer:
131
+ writer.close()
132
+ try:
133
+ await writer.wait_closed()
134
+ except:
135
+ pass
136
 
137
  async def tty_to_ws():
138
  try:
139
  while True:
140
+ # 读取 ttyd 的输出
141
  data = await reader.read(4096)
142
+ if not data:
143
  break
144
+ # 发送给前端 (作为二进制,保留所有控制字符)
145
  await ws.send_bytes(data)
146
+ except Exception as e:
147
+ print(f"TTY->WS Error: {e}")
148
  finally:
149
+ if writer:
150
+ writer.close()
151
+ try:
152
+ await writer.wait_closed()
153
+ except:
154
+ pass
155
 
156
+ # 并行运行两个方向的数据流
157
  await asyncio.gather(ws_to_tty(), tty_to_ws())
158
+
159
+ if not ws.closed:
160
+ await ws.close()
161
+
162
  return ws
163
 
164
  async def on_startup(app):
165
  # 启动 ttyd 子进程
166
+ # 使用 --writable 确保可写 (虽然默认通常是可写的,但显式指定更安全)
167
+ # 注意:如果之前的 ttyd 版本不支持某些参数,这里只保留最基础的 -p
168
  cmd = ["/usr/local/bin/ttyd", "-p", str(TTYD_PORT), "/bin/bash"]
169
  print(f"Starting ttyd: {' '.join(cmd)}")
170
+
171
+ # 启动进程
172
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
173
+ app['ttyd_proc'] = proc
174
+ print("ttyd started successfully.")
175
 
176
  def main():
177
  app = web.Application()
 
180
  app.on_startup.append(on_startup)
181
 
182
  print(f"Starting server on port {SERVER_PORT}...")
183
+ # print=None 禁止 aiohttp 自带的日志,避免干扰
184
  web.run_app(app, host='0.0.0.0', port=SERVER_PORT, print=lambda x: None)
185
 
186
  if __name__ == '__main__':