feat: add remote screenshot and shell execution capabilities
Some checks failed
Build Multi-Platform Binaries / build-frontend (push) Failing after 16s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Has been skipped
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Has been skipped

- Add screenshot capture API with quality control
- Add remote shell command execution with timeout
- Implement client-side handlers for screenshot and shell requests
- Add Web UI components for screenshot viewing and shell terminal
- Support auto-refresh for screenshot monitoring
- Add shell command history navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 21:33:09 +08:00
parent a9ca714b24
commit 5cee8daabc
11 changed files with 674 additions and 9 deletions

View File

@@ -75,6 +75,14 @@ const (
// 系统状态消息
MsgTypeSystemStatsRequest uint8 = 100 // 请求系统状态
MsgTypeSystemStatsResponse uint8 = 101 // 系统状态响应
// 截图消息
MsgTypeScreenshotRequest uint8 = 102 // 请求截图
MsgTypeScreenshotResponse uint8 = 103 // 截图响应
// Shell 执行消息
MsgTypeShellExecuteRequest uint8 = 104 // 执行 Shell 命令
MsgTypeShellExecuteResponse uint8 = 105 // Shell 执行结果
)
// Message 基础消息结构
@@ -461,3 +469,30 @@ type SystemStatsResponse struct {
DiskUsed uint64 `json:"disk_used"` // 已用磁盘 (字节)
DiskUsage float64 `json:"disk_usage"` // 磁盘使用率 (0-100)
}
// ScreenshotRequest 截图请求
type ScreenshotRequest struct {
Quality int `json:"quality"` // JPEG 质量 1-100, 0 使用默认值
}
// ScreenshotResponse 截图响应
type ScreenshotResponse struct {
Data string `json:"data"` // Base64 编码的 JPEG 图片
Width int `json:"width"` // 图片宽度
Height int `json:"height"` // 图片高度
Timestamp int64 `json:"timestamp"` // 截图时间戳
Error string `json:"error,omitempty"` // 错误信息
}
// ShellExecuteRequest Shell 执行请求
type ShellExecuteRequest struct {
Command string `json:"command"` // 要执行的命令
Timeout int `json:"timeout"` // 超时秒数, 0 使用默认值 (30秒)
}
// ShellExecuteResponse Shell 执行响应
type ShellExecuteResponse struct {
Output string `json:"output"` // stdout + stderr 组合输出
ExitCode int `json:"exit_code"` // 进程退出码
Error string `json:"error,omitempty"` // 错误信息
}

98
pkg/utils/screenshot.go Normal file
View File

@@ -0,0 +1,98 @@
package utils
import (
"bytes"
"fmt"
"image"
"image/jpeg"
"github.com/kbinani/screenshot"
)
// CaptureScreenshot 捕获主屏幕截图
// quality: JPEG 质量 (1-100), 0 使用默认值 (75)
// 返回: JPEG 图片数据, 宽度, 高度, 错误
func CaptureScreenshot(quality int) ([]byte, int, int, error) {
// 默认质量
if quality <= 0 || quality > 100 {
quality = 75
}
// 获取活动显示器数量
n := screenshot.NumActiveDisplays()
if n == 0 {
return nil, 0, 0, fmt.Errorf("no active display found")
}
// 获取主显示器边界
bounds := screenshot.GetDisplayBounds(0)
if bounds.Empty() {
return nil, 0, 0, fmt.Errorf("failed to get display bounds")
}
// 捕获屏幕
img, err := screenshot.CaptureRect(bounds)
if err != nil {
return nil, 0, 0, fmt.Errorf("capture screen: %w", err)
}
// 编码为 JPEG
var buf bytes.Buffer
opts := &jpeg.Options{Quality: quality}
if err := jpeg.Encode(&buf, img, opts); err != nil {
return nil, 0, 0, fmt.Errorf("encode jpeg: %w", err)
}
return buf.Bytes(), bounds.Dx(), bounds.Dy(), nil
}
// CaptureAllScreens 捕获所有屏幕并拼接
// quality: JPEG 质量 (1-100), 0 使用默认值 (75)
// 返回: JPEG 图片数据, 宽度, 高度, 错误
func CaptureAllScreens(quality int) ([]byte, int, int, error) {
// 默认质量
if quality <= 0 || quality > 100 {
quality = 75
}
// 获取活动显示器数量
n := screenshot.NumActiveDisplays()
if n == 0 {
return nil, 0, 0, fmt.Errorf("no active display found")
}
// 计算所有屏幕的总边界
var totalBounds image.Rectangle
for i := 0; i < n; i++ {
bounds := screenshot.GetDisplayBounds(i)
totalBounds = totalBounds.Union(bounds)
}
// 创建总画布
totalImg := image.NewRGBA(totalBounds)
// 捕获每个屏幕并绘制到总画布
for i := 0; i < n; i++ {
bounds := screenshot.GetDisplayBounds(i)
img, err := screenshot.CaptureRect(bounds)
if err != nil {
continue // 跳过失败的屏幕
}
// 绘制到总画布
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
totalImg.Set(x, y, img.At(x-bounds.Min.X, y-bounds.Min.Y))
}
}
}
// 编码为 JPEG
var buf bytes.Buffer
opts := &jpeg.Options{Quality: quality}
if err := jpeg.Encode(&buf, totalImg, opts); err != nil {
return nil, 0, 0, fmt.Errorf("encode jpeg: %w", err)
}
return buf.Bytes(), totalBounds.Dx(), totalBounds.Dy(), nil
}