588 lines
13 KiB
Markdown
588 lines
13 KiB
Markdown
# 远程屏幕监控系统
|
||
|
||
一个基于 Node.js 的实时远程屏幕监控和控制系统,支持鼠标和键盘的远程操作、剪贴板同步、文件传输等功能。
|
||
|
||
## 功能特点
|
||
|
||
- 🎥 **实时屏幕流** - 低延迟的屏幕视频流传输,使用 FFmpeg 进行 MPEG1 编码
|
||
- 🖱️ **鼠标控制** - 远程鼠标移动、点击和滚轮操作
|
||
- ⌨️ **键盘控制** - 远程键盘输入,支持特殊键和组合键
|
||
- 📋 **剪贴板同步** - 支持文本和图片的双向剪贴板同步
|
||
- **文件传输** - 支持大文件分块上传、下载和文件管理
|
||
- 🔒 **安全认证** - bcrypt 密码哈希 + JWT Token 认证
|
||
- 🌐 **内网穿透** - 集成 FRP 客户端,支持外网访问
|
||
- 📦 **Git 服务** - 可选集成 Gitea,提供代码托管服务
|
||
- 📝 **日志系统** - 完整的运行日志记录
|
||
- ⚙️ **灵活配置** - 支持配置文件和环境变量配置
|
||
|
||
## 快速开始
|
||
|
||
### 环境要求
|
||
|
||
- Node.js >= 16.0.0
|
||
- Windows 操作系统(输入控制功能需要)
|
||
- FFmpeg(已内置安装)
|
||
|
||
### 安装依赖
|
||
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
### 启动服务
|
||
|
||
```bash
|
||
npm run dev
|
||
# 或
|
||
npm start
|
||
```
|
||
|
||
访问 http://localhost:3000 查看屏幕流。
|
||
|
||
## 配置
|
||
|
||
配置文件位于 `config/default.json`,可配置项包括:
|
||
|
||
```json
|
||
{
|
||
"server": {
|
||
"port": 3000,
|
||
"host": "0.0.0.0"
|
||
},
|
||
"stream": {
|
||
"fps": 30,
|
||
"bitrate": "4000k",
|
||
"gop": 10,
|
||
"preset": "ultrafast",
|
||
"resolution": {
|
||
"width": 1920,
|
||
"height": 1080
|
||
}
|
||
},
|
||
"input": {
|
||
"mouseEnabled": true,
|
||
"keyboardEnabled": true,
|
||
"sensitivity": 1.0
|
||
},
|
||
"security": {
|
||
"password": "",
|
||
"tokenExpiry": 3600
|
||
},
|
||
"frp": {
|
||
"enabled": true,
|
||
"frpcPath": "./frp/frpc.exe",
|
||
"configPath": "./frp/frpc.toml"
|
||
},
|
||
"gitea": {
|
||
"enabled": true
|
||
}
|
||
}
|
||
```
|
||
|
||
### 配置说明
|
||
|
||
| 配置项 | 说明 | 默认值 |
|
||
|--------|------|--------|
|
||
| server.port | 服务器端口 | 3000 |
|
||
| server.host | 服务器监听地址 | 0.0.0.0 |
|
||
| stream.fps | 视频帧率 | 30 |
|
||
| stream.bitrate | 视频码率 | 4000k |
|
||
| stream.gop | GOP 大小(关键帧间隔) | 10 |
|
||
| stream.preset | 编码预设 | ultrafast |
|
||
| stream.resolution | 视频分辨率 | 1920x1080 |
|
||
| input.mouseEnabled | 是否启用鼠标控制 | true |
|
||
| input.keyboardEnabled | 是否启用键盘控制 | true |
|
||
| input.sensitivity | 鼠标灵敏度 | 1.0 |
|
||
| security.password | 访问密码(空表示不需要密码) | "" |
|
||
| security.tokenExpiry | Token 有效期(秒) | 3600 |
|
||
| frp.enabled | 是否启用 FRP 内网穿透 | true |
|
||
| gitea.enabled | 是否启用 Gitea 服务 | true |
|
||
|
||
### 环境变量配置
|
||
|
||
所有配置项都可以通过环境变量覆盖,格式为 `REMOTE_<SECTION>_<KEY>`:
|
||
|
||
```bash
|
||
# 设置服务器端口
|
||
REMOTE_SERVER_PORT=8080
|
||
|
||
# 设置密码
|
||
REMOTE_SECURITY_PASSWORD=your_password
|
||
|
||
# 设置 JWT 密钥
|
||
JWT_SECRET=your_jwt_secret
|
||
```
|
||
|
||
## API 接口
|
||
|
||
### 认证接口
|
||
|
||
#### 登录
|
||
|
||
```bash
|
||
POST /login
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"password": "your_password"
|
||
}
|
||
```
|
||
|
||
响应:设置 `auth` Cookie 并重定向到首页。
|
||
|
||
#### API 登录
|
||
|
||
```bash
|
||
POST /api/auth/login
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"password": "your_password"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"token": "jwt_token_here"
|
||
}
|
||
```
|
||
|
||
#### 验证 Token
|
||
|
||
```bash
|
||
POST /api/auth/verify
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"valid": true,
|
||
"userId": "default-user"
|
||
}
|
||
```
|
||
|
||
### 鼠标控制
|
||
|
||
```bash
|
||
POST /api/input/mouse/move
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"x": 100,
|
||
"y": 200
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/mouse/down
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"button": "left" # "left", "right" 或 "middle"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/mouse/up
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"button": "left"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/mouse/click
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"button": "left"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/mouse/wheel
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"delta": 120 # 正值向上滚动,负值向下滚动
|
||
}
|
||
```
|
||
|
||
### 键盘控制
|
||
|
||
```bash
|
||
POST /api/input/keyboard/down
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"key": "enter"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/keyboard/up
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"key": "enter"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/keyboard/press
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"key": "enter"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/input/keyboard/type
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"text": "Hello World"
|
||
}
|
||
```
|
||
|
||
支持的特殊键:`enter`, `backspace`, `tab`, `escape`, `delete`, `home`, `end`, `pageup`, `pagedown`, `up`, `down`, `left`, `right`, `f1`-`f12`, `ctrl`, `alt`, `shift`, `win`, `space`
|
||
|
||
### 流媒体接口
|
||
|
||
```bash
|
||
GET /api/stream/info
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"stream": {
|
||
"status": "running",
|
||
"resolution": {
|
||
"width": 1920,
|
||
"height": 1080
|
||
},
|
||
"fps": 30,
|
||
"bitrate": "4000k",
|
||
"gop": 10,
|
||
"encoder": "mpeg1video"
|
||
}
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/stream/start
|
||
```
|
||
|
||
```bash
|
||
POST /api/stream/stop
|
||
```
|
||
|
||
### 文件传输接口
|
||
|
||
```bash
|
||
GET /api/files
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"files": [
|
||
{
|
||
"name": "example.txt",
|
||
"size": 1024,
|
||
"modified": "2026-03-05T10:00:00.000Z",
|
||
"type": ".txt"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
```bash
|
||
GET /api/files/browse?path=relative/path
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"name": "folder",
|
||
"isDirectory": true,
|
||
"size": 0,
|
||
"modified": "2026-03-05T10:00:00.000Z",
|
||
"type": "directory"
|
||
}
|
||
],
|
||
"currentPath": "relative/path",
|
||
"parentPath": ""
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/files/upload/start
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"filename": "large_file.zip",
|
||
"totalChunks": 10,
|
||
"fileSize": 50000000
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"fileId": "abc123",
|
||
"chunkSize": 5242880,
|
||
"message": "Upload session started"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
POST /api/files/upload/chunk
|
||
Content-Type: multipart/form-data
|
||
|
||
fileId: abc123
|
||
chunkIndex: 0
|
||
chunk: <binary data>
|
||
```
|
||
|
||
```bash
|
||
POST /api/files/upload/merge
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"fileId": "abc123",
|
||
"totalChunks": 10,
|
||
"filename": "large_file.zip"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
GET /api/files/:filename
|
||
```
|
||
|
||
支持 Range 请求头进行断点续传。
|
||
|
||
```bash
|
||
DELETE /api/files/:filename
|
||
```
|
||
|
||
### WebSocket 消息类型
|
||
|
||
连接地址:`ws://localhost:3000/ws`
|
||
|
||
#### 客户端发送
|
||
|
||
| 类型 | 说明 | 数据 |
|
||
|------|------|------|
|
||
| mouseMove | 鼠标移动 | `{ type: "mouseMove", x: 100, y: 200 }` |
|
||
| mouseDown | 鼠标按下 | `{ type: "mouseDown", button: "left" }` |
|
||
| mouseUp | 鼠标释放 | `{ type: "mouseUp", button: "left" }` |
|
||
| mouseWheel | 鼠标滚轮 | `{ type: "mouseWheel", delta: 120 }` |
|
||
| keyDown | 键盘按下 | `{ type: "keyDown", key: "enter" }` |
|
||
| keyUp | 键盘释放 | `{ type: "keyUp", key: "enter" }` |
|
||
| clipboardGet | 获取剪贴板 | `{ type: "clipboardGet" }` |
|
||
| clipboardSet | 设置剪贴板 | `{ type: "clipboardSet", contentType: "text", data: "content" }` |
|
||
|
||
#### 服务端发送
|
||
|
||
| 类型 | 说明 | 数据 |
|
||
|------|------|------|
|
||
| screenInfo | 屏幕信息 | `{ type: "screenInfo", width: 1920, height: 1080 }` |
|
||
| clipboardData | 剪贴板数据 | `{ type: "clipboardData", contentType: "text", data: "content", size: 100 }` |
|
||
| clipboardResult | 剪贴板操作结果 | `{ type: "clipboardResult", success: true }` |
|
||
| clipboardTooLarge | 剪贴板内容过大 | `{ type: "clipboardTooLarge", size: 1000000 }` |
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
remote/
|
||
├── src/
|
||
│ ├── config/ # 配置管理
|
||
│ │ ├── index.js # 配置加载器
|
||
│ │ └── schema.js # 配置验证模式
|
||
│ ├── controllers/ # 控制器层
|
||
│ │ ├── AuthController.js # 认证控制器
|
||
│ │ ├── InputController.js# 输入控制器
|
||
│ │ └── StreamController.js# 流媒体控制器
|
||
│ ├── core/ # 核心模块
|
||
│ │ ├── App.js # 应用主类
|
||
│ │ ├── Container.js # 依赖注入容器
|
||
│ │ ├── EventBus.js # 事件总线
|
||
│ │ ├── ErrorHandler.js # 错误处理器
|
||
│ │ └── events.js # 事件类型定义
|
||
│ ├── middlewares/ # 中间件
|
||
│ │ ├── auth.js # 认证中间件
|
||
│ │ ├── error.js # 错误处理中间件
|
||
│ │ └── rateLimit.js # 限流中间件
|
||
│ ├── routes/ # 路由层
|
||
│ │ ├── index.js # 路由汇总
|
||
│ │ ├── auth.js # 认证路由
|
||
│ │ ├── files.js # 文件路由
|
||
│ │ ├── input.js # 输入路由
|
||
│ │ └── stream.js # 流媒体路由
|
||
│ ├── server/ # 服务器层
|
||
│ │ ├── Server.js # HTTP 服务器
|
||
│ │ ├── WebSocketServer.js# WebSocket 服务器
|
||
│ │ ├── StreamBroadcaster.js# 流广播器
|
||
│ │ ├── InputHandler.js # 输入处理器
|
||
│ │ └── messageTypes.js # 消息类型定义
|
||
│ ├── services/ # 服务层
|
||
│ │ ├── auth/ # 认证服务
|
||
│ │ │ ├── AuthService.js
|
||
│ │ │ └── TokenManager.js
|
||
│ │ ├── clipboard/ # 剪贴板服务
|
||
│ │ │ └── ClipboardService.js
|
||
│ │ ├── file/ # 文件服务
|
||
│ │ │ └── FileService.js
|
||
│ │ ├── input/ # 输入服务
|
||
│ │ │ ├── InputService.js
|
||
│ │ │ └── PowerShellInput.js
|
||
│ │ ├── network/ # 网络服务
|
||
│ │ │ ├── FRPService.js
|
||
│ │ │ └── GiteaService.js
|
||
│ │ ├── stream/ # 流媒体服务
|
||
│ │ │ ├── FFmpegEncoder.js
|
||
│ │ │ ├── ScreenCapture.js
|
||
│ │ │ └── StreamService.js
|
||
│ │ └── index.js # 服务汇总
|
||
│ ├── utils/ # 工具类
|
||
│ │ ├── config.js # 配置工具
|
||
│ │ ├── logger.js # 日志工具
|
||
│ │ └── paths.js # 路径工具
|
||
│ └── index.js # 应用入口
|
||
├── config/
|
||
│ └── default.json # 默认配置
|
||
├── docs/ # 文档目录
|
||
│ ├── 开发/ # 开发文档
|
||
│ └── 指南/ # 使用指南
|
||
├── frp/ # FRP 内网穿透
|
||
│ ├── frpc.exe
|
||
│ ├── frpc.toml
|
||
│ └── frpc-runtime.toml
|
||
├── gitea/ # Gitea Git 服务
|
||
├── logs/ # 日志目录
|
||
│ ├── combined.log # 所有日志
|
||
│ └── error.log # 错误日志
|
||
├── public/ # 前端静态文件
|
||
│ ├── css/
|
||
│ │ └── main.css
|
||
│ ├── js/
|
||
│ │ ├── app.js # 应用入口
|
||
│ │ ├── file-panel.js # 文件传输面板
|
||
│ │ ├── input.js # 输入处理
|
||
│ │ ├── jsmpeg.min.js # JSMpeg 播放器
|
||
│ │ ├── player.js # 视频播放器
|
||
│ │ └── utils.js # 工具函数
|
||
│ └── index.html
|
||
├── scripts/
|
||
│ └── migrate-password.js # 密码迁移脚本
|
||
├── uploads/ # 上传文件目录
|
||
├── .gitignore
|
||
├── package.json
|
||
└── README.md
|
||
```
|
||
|
||
## 构建与部署
|
||
|
||
### 开发环境
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
### 生产环境打包
|
||
|
||
使用 pkg 打包为可执行文件:
|
||
|
||
```bash
|
||
npm run build
|
||
```
|
||
|
||
打包后的文件位于 `dist/` 目录:
|
||
- `remote-screen-monitor.exe` - 主程序
|
||
- `public/` - 前端静态文件
|
||
- `config/` - 配置文件
|
||
- `frp/` - FRP 客户端
|
||
- `ffmpeg.exe` - FFmpeg 编码器
|
||
|
||
### Windows 服务安装
|
||
|
||
使用 NSSM 将应用安装为 Windows 服务:
|
||
|
||
```powershell
|
||
# 安装服务
|
||
nssm install RemoteApp "C:\path\to\remote-screen-monitor.exe"
|
||
|
||
# 设置工作目录
|
||
nssm set RemoteApp AppDirectory "C:\path\to\app"
|
||
|
||
# 设置启动类型
|
||
nssm set RemoteApp Start SERVICE_AUTO_START
|
||
|
||
# 启动服务
|
||
nssm start RemoteApp
|
||
```
|
||
|
||
详细说明请参考 [NSSM使用指南](docs/指南/NSSM使用指南.md)。
|
||
|
||
## 安全注意事项
|
||
|
||
⚠️ **重要提示**:
|
||
- 在公共网络上使用时,务必设置密码保护
|
||
- 建议使用 HTTPS/WSS(通过反向代理如 Nginx)
|
||
- 不要使用 root/Administrator 权限运行此服务
|
||
- 定期更换密码和 JWT 密钥
|
||
|
||
### 密码安全
|
||
|
||
系统支持 bcrypt 密码哈希。使用迁移脚本生成安全的密码哈希:
|
||
|
||
```bash
|
||
node scripts/migrate-password.js
|
||
```
|
||
|
||
将生成的哈希值设置到环境变量 `REMOTE_SECURITY_PASSWORD` 中。
|
||
|
||
## 故障排除
|
||
|
||
### 鼠标/键盘控制不工作
|
||
|
||
确保以管理员权限运行(Windows),某些操作可能需要提升的权限。
|
||
|
||
### 视频流卡顿
|
||
|
||
- 降低 FPS 或分辨率
|
||
- 降低视频码率
|
||
- 检查网络带宽
|
||
|
||
### 日志查看
|
||
|
||
日志文件位于 `logs/` 目录:
|
||
- `logs/combined.log` - 所有日志
|
||
- `logs/error.log` - 错误日志
|
||
|
||
## 技术栈
|
||
|
||
- **后端**: Node.js, Express 5.x, ws (WebSocket), winston (日志)
|
||
- **前端**: HTML5 Canvas, JSMpeg 播放器
|
||
- **视频编码**: FFmpeg (mpeg1video)
|
||
- **输入模拟**: PowerShell + Windows API (user32.dll)
|
||
- **认证**: bcrypt, jsonwebtoken
|
||
- **文件处理**: multer, fs-extra
|
||
- **内网穿透**: FRP (Fast Reverse Proxy)
|
||
- **打包**: pkg
|
||
|
||
## 许可证
|
||
|
||
ISC
|