[Nutria] - Terminal App

163 阅读1分钟

caploon是基于unix。为了实现在web下面实现运行unix命令终端,api deamon 提高了websocket的接口:ws://127.0.0.1:7703,这是Rust实现的wspty。前端使用Xterm库显示命令行。

前端

websockect


  var websocket = new WebSocket("ws://127.0.0.1:7703");
  websocket.binaryType = "arraybuffer";
  
  // websocket event
  
  // onopen
    // 默认安卓的sh
    let command = "/system/bin/sh";
    if (location.port == 8081) {
      // use /usr/bin/bash on Linux and /bin/zsh on Mac
      if (navigator.platform.startsWith("Linux")) {
        command = "/usr/bin/bash";
      } else if (navigator.platform.startsWith("Mac")) {
        command = "/bin/zsh";
      } else {
        console.error(`Unsupported platform: ${navigator.platform}`);
        return;
      }
    }
   
   // 发送
   websocket.send(command);
   // 初始化 xterm
    term = new Terminal({
      screenKeys: true,
      useStyle: true,
      cursorBlink: true,
      fontFamily: "Droid Sans Mono, mono, monospace",
      windowsMode: true,
    });
    
  // onmessage
    websocket.onmessage = (event) => {
      let data = event.data;

      term.write(
        typeof data === "string" ? data : new Uint8Array(data.slice(1))
      );
    };
    
   

Xterm

    //string 命令数据
    term.onData(function (data) {
      websocket.send(new TextEncoder().encode("\x00" + data));
    });
    
    // Uint8Array数据
    term.onBinary((data) => {
      if (websocket.readyState !== 1) {
        return;
      }
      const buffer = new Uint8Array(data.length + 1);
      buffer[0] = 0;
      for (let i = 0; i < data.length; ++i) {
        buffer[i + 1] = data.charCodeAt(i) & 255;
      }
      websocket.send(buffer);
    });

    term.onResize((event) => {
      websocket.send(
        new TextEncoder().encode(
          "\x01" + JSON.stringify({ cols: event.cols, rows: event.rows })
        )
      );
    });

    term.onTitleChange(function (title) {
      document.title = `webPTY | ${title}`;
    });


后端 api-daemon 接口

wspty库: capyloon/wspty (github.com)
api-daemon 在main开辟进程给wspty:api-daemon/daemon/src/main.rs

fn start_wspty() {
    let _ = thread::Builder::new()
        .name("wspty server".into())
        .spawn(move || {
            debug!("start_wspty start");
            tokio::runtime::Runtime::new().unwrap().block_on(async {
                let _ = wspty::start_server()
                    .await
                    .map_err(|e| error!("wspty server exit with error: {:?}", e));
            });
            debug!("start_wspty end");
        });
}