UNPKG

hudada-cli

Version:

专为程序员准备的本地文档搜索,快捷开发工具

3 lines (2 loc) 5.76 kB
#!/usr/bin/env node import{createServer as e}from"http";import{Server as o}from"socket.io";import{Client as r}from"ssh2";import n from"chalk";import t,{join as s}from"path";import{fileURLToPath as l,parse as i}from"url";import{createReadStream as a}from"fs";import c from"open";const d=l(import.meta.url),m=s(d,"..");async function h(l=669){const d=e(((e,o)=>{const r=i(e.url||"").pathname||"/";o.setHeader("Access-Control-Allow-Origin","*"),o.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),o.setHeader("Access-Control-Allow-Headers","Content-Type");try{if("/"===r)return o.writeHead(200,{"Content-Type":"text/html"}),console.log(m,"__dirname"),void a(s(m,"./ssh/client/index.html")).pipe(o);if(r.endsWith(".js"))return o.writeHead(200,{"Content-Type":"application/javascript"}),void a(s(m,"./ssh/client",r)).pipe(o);if(r.endsWith(".css"))return o.writeHead(200,{"Content-Type":"text/css"}),void a(s(m,"./ssh/client",r)).pipe(o);o.writeHead(404),o.end("Not Found")}catch(e){console.error("Static file error:",e),o.writeHead(500),o.end("Internal Server Error")}})),u=new o(d,{cors:{origin:"*",methods:["GET","POST"]},transports:["websocket","polling"],path:"/socket.io/"}),f=new Map;u.on("connection",(e=>{console.log(n.green("Client connected"));let o=null;e.on("ssh:connect",(async t=>{try{o=new r,o.on("ready",(()=>{console.log(n.blue(`SSH connection established to ${t.host}`)),e.emit("ssh:ready"),o?.shell({term:"xterm-256color"},((r,n)=>{r?e.emit("ssh:error",r.message):(n.on("data",(o=>{e.emit("ssh:data",o.toString("utf-8"))})),n.on("error",(o=>{e.emit("ssh:error",o.message)})),n.on("close",(()=>{e.emit("ssh:close"),o&&(o.end(),o=null)})),e.on("ssh:data",(e=>{n&&!n.closed&&n.write(e)})),e.on("ssh:resize",(({rows:e,cols:o})=>{n&&!n.closed&&n.setWindow(e,o,0,0)})))}))})),o.on("error",(o=>{console.error(n.red("SSH connection error:"),o),e.emit("ssh:error",o.message)})),o.on("close",(()=>{console.log(n.yellow("SSH connection closed")),e.emit("ssh:close"),o=null})),o.connect({host:t.host,port:t.port,username:t.username,password:t.password,readyTimeout:2e4,keepaliveInterval:1e4})}catch(o){console.error(n.red("SSH connection error:"),o),e.emit("ssh:error",o instanceof Error?o.message:"Unknown error")}})),e.on("upload-start",(async o=>{try{console.log(n.blue(`Starting upload for ${o.filename}`)),f.set(o.filename,{filename:o.filename,chunks:new Array(o.totalChunks),totalChunks:o.totalChunks,receivedChunks:0}),e.emit("upload-started",{filename:o.filename})}catch(o){console.error(n.red("Upload start error:"),o),e.emit("upload-error",{message:`Upload start failed: ${o.message}`})}})),e.on("upload-chunk",(async o=>{try{const r=f.get(o.filename);if(!r)throw new Error("Upload session not found");const n=Buffer.from(o.content,"base64");r.chunks[o.chunkIndex]=n,r.receivedChunks++,e.emit("chunk-received",{filename:o.filename,chunkIndex:o.chunkIndex})}catch(o){e.emit("upload-error",{message:`Chunk upload failed: ${o.message}`})}})),e.on("upload-complete",(async r=>{try{const t=f.get(r.filename);if(!t)throw new Error("Upload session not found");const s=Buffer.concat(t.chunks);if(s.length!==r.totalSize)throw new Error("File size mismatch");const l=await new Promise(((e,r)=>{o?.sftp(((o,n)=>{o?r(o):e(n)}))}));await new Promise(((e,o)=>{const n=l.createWriteStream(r.filename);n.on("error",o),n.on("close",e),n.end(s)})),f.delete(r.filename),e.emit("upload-success",{filename:r.filename,size:s.length}),console.log(n.green(`File ${r.filename} uploaded successfully (${s.length} bytes)`))}catch(o){console.error(n.red("Upload complete error:"),o),e.emit("upload-error",{message:o instanceof Error?o.message:"Unknown error"})}})),e.on("ssh:disconnect",(()=>{o&&(o.end(),o=null)})),e.on("disconnect",(()=>{console.log(n.yellow("Client disconnected")),o&&(o.end(),o=null)})),e.on("download-file",(async r=>{try{if(!o)throw new Error("SSH connection not found");let s=r.filepath.trim();s.startsWith("~")&&(s=s.replace("~","/root")),s.startsWith("/")||(s=`/root/${s}`),console.log("Starting download for file:",s);const l=await new Promise(((e,r)=>{o?.sftp(((o,n)=>{o?(console.error("SFTP error:",o),r(o)):e(n)}))})),i=await new Promise(((e,o)=>{l.stat(s,((r,n)=>{r?(console.error("File stat error:",r),o(new Error(`File not found: ${s}`))):n.isFile()?e(n):o(new Error("Not a file"))}))})),a=5242880,c=i.size,d=Math.ceil(c/a);console.log(`File size: ${c} bytes, Total chunks: ${d}`),e.emit("file-info",{filename:t.basename(s),size:c,totalChunks:d}),await new Promise((o=>{e.once("ready-to-receive",(()=>{console.log("Client ready to receive"),o()}))}));for(let o=0;o<d;o++){const r=o*a,n=Math.min(r+a,c);console.log(`Sending chunk ${o+1}/${d} (${r}-${n})`);try{const t=await new Promise(((e,t)=>{const i=l.createReadStream(s,{start:r,end:n-1}),a=[];i.on("data",(e=>a.push(e))),i.on("end",(()=>e(Buffer.concat(a)))),i.on("error",(e=>{console.error(`Error reading chunk ${o}:`,e),t(e)}))}));await new Promise(((r,n)=>{e.emit("file-chunk",{chunkIndex:o,content:t.toString("base64"),isLast:o===d-1}),e.once("chunk-received",(()=>{console.log(`Chunk ${o+1}/${d} confirmed`),r()})),setTimeout((()=>{n(new Error(`Chunk ${o} confirmation timeout`))}),1e4)}))}catch(e){throw console.error(`Error processing chunk ${o}:`,e),e}}console.log(n.green(`File ${s} downloaded successfully (${c} bytes)`))}catch(o){console.error(n.red("Download error:"),o),e.emit("download-error",{message:o instanceof Error?o.message:"Download failed"})}}))})),d.listen(l,(()=>{console.log(n.green(`SSH WebSocket server running at http://localhost:${l}`)),c(`http://localhost:${l}`)})),d.on("error",(e=>{"EADDRINUSE"===e.code?(console.log(n.yellow(`Port ${l} is in use, trying ${l+1}`)),h(l+1)):console.error(n.red("Server error:"),e)}))}export{h as startSSHServer};