mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-12 16:00:27 +00:00
feat: 系统终端
This commit is contained in:
88
napcat.webui/src/controllers/terminal_manager.ts
Normal file
88
napcat.webui/src/controllers/terminal_manager.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
type TerminalCallback = (data: string) => void
|
||||
|
||||
interface TerminalConnection {
|
||||
ws: WebSocket
|
||||
callbacks: Set<TerminalCallback>
|
||||
isConnected: boolean
|
||||
buffer: string[] // 添加缓存数组
|
||||
}
|
||||
|
||||
class TerminalManager {
|
||||
private connections: Map<string, TerminalConnection> = new Map()
|
||||
private readonly MAX_BUFFER_SIZE = 1000 // 限制缓存大小
|
||||
|
||||
connectTerminal(id: string, callback: TerminalCallback): WebSocket {
|
||||
let conn = this.connections.get(id)
|
||||
|
||||
if (!conn) {
|
||||
const url = new URL(window.location.href)
|
||||
url.protocol = url.protocol.replace('http', 'ws')
|
||||
url.pathname = `/api/ws/terminal`
|
||||
url.searchParams.set('id', id)
|
||||
const token = JSON.parse(localStorage.getItem('token') || '')
|
||||
if (!token) {
|
||||
throw new Error('No token found')
|
||||
}
|
||||
url.searchParams.set('token', token)
|
||||
const ws = new WebSocket(url.toString())
|
||||
conn = {
|
||||
ws,
|
||||
callbacks: new Set([callback]),
|
||||
isConnected: false,
|
||||
buffer: [] // 初始化缓存
|
||||
}
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const data = event.data
|
||||
// 保存到缓存
|
||||
conn?.buffer.push(data)
|
||||
if ((conn?.buffer.length ?? 0) > this.MAX_BUFFER_SIZE) {
|
||||
conn?.buffer.shift()
|
||||
}
|
||||
conn?.callbacks.forEach((cb) => cb(data))
|
||||
}
|
||||
|
||||
ws.onopen = () => {
|
||||
if (conn) conn.isConnected = true
|
||||
}
|
||||
|
||||
ws.onclose = () => {
|
||||
if (conn) conn.isConnected = false
|
||||
}
|
||||
|
||||
this.connections.set(id, conn)
|
||||
} else {
|
||||
conn.callbacks.add(callback)
|
||||
// 恢复历史内容
|
||||
conn.buffer.forEach((data) => callback(data))
|
||||
}
|
||||
|
||||
return conn.ws
|
||||
}
|
||||
|
||||
disconnectTerminal(id: string, callback: TerminalCallback) {
|
||||
const conn = this.connections.get(id)
|
||||
if (!conn) return
|
||||
|
||||
conn.callbacks.delete(callback)
|
||||
}
|
||||
|
||||
removeTerminal(id: string) {
|
||||
const conn = this.connections.get(id)
|
||||
if (conn?.ws.readyState === WebSocket.OPEN) {
|
||||
conn.ws.close()
|
||||
}
|
||||
this.connections.delete(id)
|
||||
}
|
||||
|
||||
sendInput(id: string, data: string) {
|
||||
const conn = this.connections.get(id)
|
||||
if (conn?.ws.readyState === WebSocket.OPEN) {
|
||||
conn.ws.send(JSON.stringify({ type: 'input', data }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const terminalManager = new TerminalManager()
|
||||
|
||||
export default terminalManager
|
||||
@@ -150,41 +150,6 @@ export default class WebUIManager {
|
||||
return data.data
|
||||
}
|
||||
|
||||
public static async sendTerminalInput(
|
||||
id: string,
|
||||
input: string
|
||||
): Promise<void> {
|
||||
await serverRequest.post(`/Log/terminal/${id}/input`, { input })
|
||||
}
|
||||
|
||||
public static getTerminalStream(id: string, onData: (data: string) => void) {
|
||||
const token = localStorage.getItem('token')
|
||||
if (!token) throw new Error('未登录')
|
||||
|
||||
const _token = JSON.parse(token)
|
||||
const eventSource = new EventSourcePolyfill(
|
||||
`/api/Log/terminal/${id}/stream`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${_token}`,
|
||||
Accept: 'text/event-stream'
|
||||
},
|
||||
withCredentials: true
|
||||
}
|
||||
)
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
try {
|
||||
const { data } = JSON.parse(event.data)
|
||||
onData(data)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return eventSource
|
||||
}
|
||||
|
||||
public static async closeTerminal(id: string): Promise<void> {
|
||||
await serverRequest.post(`/Log/terminal/${id}/close`)
|
||||
}
|
||||
@@ -205,9 +170,15 @@ export default class WebUIManager {
|
||||
if (!token) throw new Error('未登录')
|
||||
|
||||
const _token = JSON.parse(token)
|
||||
const ws = new WebSocket(
|
||||
`ws://${window.location.host}/api/ws/terminal?id=${id}&token=${_token}`
|
||||
)
|
||||
|
||||
const url = new URL(window.location.origin)
|
||||
url.protocol = "ws://"
|
||||
url.pathname = "/api/ws/terminal"
|
||||
url.searchParams.set('token', _token)
|
||||
url.searchParams.set("id", id)
|
||||
console.log(url.toString())
|
||||
|
||||
const ws = new WebSocket(url.toString())
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user