Compare commits
19
Commits
e87e5fc9de
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bed4c73eb | ||
|
|
78f7a5f46e | ||
|
|
05a4fd960d | ||
|
|
ea6afcc08c | ||
|
|
dd4a156b9c | ||
|
|
e03a7a141c | ||
|
|
ecca396792 | ||
|
|
57ca2f1ea3 | ||
|
|
c395c3fc4e | ||
|
|
d8ef61adec | ||
|
|
5d7ca83162 | ||
|
|
993b5ee4c0 | ||
|
|
ae27984fb0 | ||
|
|
4aa93d8a9d | ||
|
|
d1a87556c5 | ||
|
|
557049b308 | ||
|
|
15b15b79c9 | ||
|
|
deafe6b1c4 | ||
|
|
db3c32a871 |
@@ -1,45 +1,48 @@
|
|||||||
# Node.js 4G SMS Gateway
|
# SMS Forwarding
|
||||||
|
|
||||||
基于Node.js的4G短信网关,通过串口与4G模块通信,接收短信并转发到多种推送渠道。
|
一个运行在 Node.js 上的 4G 短信网关。程序通过串口控制 4G 模组,接收 PDU 短信,合并长短信,并把短信转发到邮件、钉钉、飞书、Telegram、PushPlus、Server 酱、Bark 或自定义 Webhook。
|
||||||
|
|
||||||
## 功能特性
|
项目同时提供 Web 管理台和 HTTP API,可用于查看模组状态、收件箱、日志、推送通道,发送短信,切换发送短信使用的 SIM,以及执行 AT 命令。
|
||||||
|
|
||||||
- ✅ 4G模块AT指令交互
|
## 功能
|
||||||
- ✅ 自动接收短信(PDU模式)
|
|
||||||
- ✅ 长短信自动合并
|
- 通过 AT 指令和 4G 模组通信
|
||||||
- ✅ 多通道推送(邮件/钉钉/飞书/Telegram等)
|
- 自动探测可用 AT 串口
|
||||||
- ✅ 短信发送功能
|
- PDU 模式接收短信
|
||||||
- ✅ RESTful API接口
|
- 长短信自动合并
|
||||||
- ✅ Web管理界面
|
- 收件箱持久化到本地文件
|
||||||
|
- 多推送通道转发短信
|
||||||
|
- Web 管理台
|
||||||
|
- Token 鉴权的 HTTP API
|
||||||
|
- Web/API 发送短信
|
||||||
|
- 双 SIM 状态识别和发送前切卡
|
||||||
|
- 移动数据开关和启动断网保护
|
||||||
|
- AT 命令调试入口
|
||||||
|
|
||||||
## 环境要求
|
## 环境要求
|
||||||
|
|
||||||
- Node.js >= 18.x
|
- Node.js 18 或更高版本
|
||||||
- 4G模块通过USB转串口连接到服务器
|
- 可通过串口访问的 4G 模组
|
||||||
- 支持的4G模块:ML307R-DC、SIM7600等AT指令兼容模块
|
- 已插入可收发短信的 SIM 卡
|
||||||
|
- Linux 下通常需要访问 `/dev/ttyUSB*` 的权限
|
||||||
|
|
||||||
## 安装
|
已主要围绕 ML307A 调试。其他 AT 指令兼容模组可以运行,但双卡、移动数据、URC 卡槽识别等能力取决于模组返回的指令结果。
|
||||||
|
|
||||||
|
## 快速启动
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
|
||||||
|
|
||||||
## 配置
|
|
||||||
|
|
||||||
复制配置模板:
|
|
||||||
```bash
|
|
||||||
cp config.example.json config.json
|
cp config.example.json config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
`config.json` 必须存在,否则程序会直接退出。最小可运行配置如下:
|
编辑 `config.json`,至少确认:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"serial": {
|
"serial": {
|
||||||
"path": "/dev/ttyUSB0",
|
"path": "/dev/ttyUSB0",
|
||||||
"baudRate": 115200,
|
"baudRate": 115200,
|
||||||
"autoDetect": true,
|
"autoDetect": true
|
||||||
"probeTimeout": 1200
|
|
||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"port": 3000,
|
"port": 3000,
|
||||||
@@ -48,164 +51,136 @@ cp config.example.json config.json
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
完整配置可参考 `config.example.json`。主要配置项如下:
|
启动:
|
||||||
|
|
||||||
| 配置项 | 是否必填 | 说明 |
|
|
||||||
|--------|----------|------|
|
|
||||||
| `serial.path` | 是 | 串口路径;Linux 常用 `/dev/ttyUSB0`,Windows 使用 `COM3` 等。`autoDetect=true` 时作为优先探测项,`autoDetect=false` 时作为实际串口。 |
|
|
||||||
| `serial.baudRate` | 是 | 串口波特率,通常为 `115200`。 |
|
|
||||||
| `serial.autoDetect` | 否 | 是否自动探测可响应 `AT` 的串口,默认按非 `false` 处理。 |
|
|
||||||
| `serial.probeTimeout` | 否 | 自动探测单个串口的超时时间,单位毫秒,默认 `1200`。 |
|
|
||||||
| `api.port` | 是 | API 和 Web 管理台监听端口。 |
|
|
||||||
| `api.webToken` | 是 | 唯一鉴权凭据;已取消 `admin/admin123` Basic Auth。 |
|
|
||||||
| `mobileData.cid` | 否 | 移动数据 PDP CID,默认 `1`;有效范围 `1-15`,不要使用 `8`。 |
|
|
||||||
| `simCards` | 否 | SIM 卡显示手机号,`slot=0` 是 SIM1,`slot=1` 是 SIM2。 |
|
|
||||||
| `smtp` | 否 | 邮件通知配置;填写 `smtp.server` 后启用。 |
|
|
||||||
| `inbox` | 否 | Web 收件箱持久化和双卡显示策略。 |
|
|
||||||
| `pushChannels` | 否 | 推送通道列表;通道开启后才要求目标字段。 |
|
|
||||||
|
|
||||||
`api.webToken` 可以通过以下方式传入:
|
|
||||||
|
|
||||||
- `Authorization: Bearer <token>`
|
|
||||||
- `X-Web-Token: <token>`
|
|
||||||
- `?token=<token>`
|
|
||||||
- Web 管理台 token 登录页
|
|
||||||
|
|
||||||
### 完整配置示例
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"serial": {
|
|
||||||
"path": "/dev/ttyUSB0",
|
|
||||||
"baudRate": 115200,
|
|
||||||
"autoDetect": true,
|
|
||||||
"probeTimeout": 1200
|
|
||||||
},
|
|
||||||
"mobileData": {
|
|
||||||
"cid": 1
|
|
||||||
},
|
|
||||||
"simCards": [
|
|
||||||
{ "slot": 0, "phoneNumber": "+8613800138000" },
|
|
||||||
{ "slot": 1, "phoneNumber": "+447700900000" }
|
|
||||||
],
|
|
||||||
"smtp": {
|
|
||||||
"server": "smtp.qq.com",
|
|
||||||
"port": 465,
|
|
||||||
"user": "your@qq.com",
|
|
||||||
"pass": "your_auth_code",
|
|
||||||
"sendTo": "recipient@example.com"
|
|
||||||
},
|
|
||||||
"inbox": {
|
|
||||||
"maxReceivedMessages": 200,
|
|
||||||
"receivedMessagesFile": "data/received-messages.json",
|
|
||||||
"partitionBySim": true,
|
|
||||||
"allowAllSimMessages": false,
|
|
||||||
"dualSimDefaultScope": "all"
|
|
||||||
},
|
|
||||||
"pushChannels": [
|
|
||||||
{
|
|
||||||
"enabled": true,
|
|
||||||
"type": "dingtalk",
|
|
||||||
"name": "钉钉通知",
|
|
||||||
"url": "https://oapi.dingtalk.com/robot/send?access_token=xxx",
|
|
||||||
"secret": "SECxxx"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api": {
|
|
||||||
"port": 3000,
|
|
||||||
"webToken": "change-this-token"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 可选项细节
|
|
||||||
|
|
||||||
- `smtp`:不需要邮件时可以删除整个对象,或清空 `server`。启用邮件时需要正确填写 `server`、`port`、`user`、`pass`、`sendTo`。
|
|
||||||
- `inbox.maxReceivedMessages`:默认 `200`,最大按 `5000` 处理。
|
|
||||||
- `inbox.receivedMessagesFile`:默认 `data/received-messages.json`,相对路径会基于项目根目录解析。
|
|
||||||
- `inbox.partitionBySim`:默认 `true`,按当前 SIM 隔离历史短信。
|
|
||||||
- `inbox.allowAllSimMessages`:默认 `false`,控制是否允许显式查询全部 SIM 历史。
|
|
||||||
- `inbox.dualSimDefaultScope`:可选 `all` 或 `current`;`current` 表示双卡时默认只看当前 SIM。
|
|
||||||
- `simCards`:只影响管理台显示,不影响短信收发。代码也兼容旧字段名 `simSlots`、`sims`、`sim`。
|
|
||||||
|
|
||||||
## 运行
|
|
||||||
|
|
||||||
### 开发模式
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 生产模式
|
|
||||||
```bash
|
```bash
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
### 使用PM2(推荐)
|
访问管理台:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:3000/admin
|
||||||
|
```
|
||||||
|
|
||||||
|
第一次访问会要求输入 `api.webToken`。也可以临时通过 URL 传入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:3000/admin?token=change-this-token
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker 运行
|
||||||
|
|
||||||
|
先准备配置:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cp config.example.json config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
然后启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
查看日志:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f sms-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
`docker-compose.yml` 会挂载:
|
||||||
|
|
||||||
|
- `./config.json:/app/config.json`
|
||||||
|
- `./logs:/app/logs`
|
||||||
|
- `./data:/app/data`
|
||||||
|
- `/dev:/dev`
|
||||||
|
- `/sys:/sys:ro`
|
||||||
|
|
||||||
|
默认端口是 `3000`。如需修改宿主机端口:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SMS_GATEWAY_PORT=8080 docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## PM2 运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
cp config.example.json config.json
|
||||||
pm2 start ecosystem.config.js
|
pm2 start ecosystem.config.js
|
||||||
pm2 save
|
pm2 save
|
||||||
pm2 startup
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## API接口
|
## 配置
|
||||||
|
|
||||||
### 查询状态
|
`config.json` 必须存在,否则程序会退出。完整模板见 `config.example.json`。
|
||||||
```bash
|
|
||||||
GET /api/status
|
|
||||||
```
|
|
||||||
|
|
||||||
### 发送短信
|
### 必填项
|
||||||
```bash
|
|
||||||
POST /api/sms/send
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
| 配置项 | 说明 |
|
||||||
"phone": "13800138000",
|
| --- | --- |
|
||||||
"message": "测试短信"
|
| `serial.path` | 串口路径。Linux 常见为 `/dev/ttyUSB0`,Windows 可写 `COM3`。`serial.autoDetect=true` 时它是优先探测项;`false` 时它就是实际打开的串口。 |
|
||||||
}
|
| `serial.baudRate` | 串口波特率,通常是 `115200`。 |
|
||||||
```
|
| `api.port` | Web 管理台和 HTTP API 监听端口。 |
|
||||||
|
| `api.webToken` | 唯一访问凭据。项目没有默认账号密码,也没有 `admin/admin123` Basic Auth。 |
|
||||||
|
|
||||||
### 查询收到的短信
|
### 可选项
|
||||||
```bash
|
|
||||||
GET /api/sms/received?limit=50
|
|
||||||
```
|
|
||||||
|
|
||||||
双卡双待模式下,默认返回全部 SIM 历史;显式传 `scope=current` 时才按当前卡槽过滤。短信记录会保留 `simSlot`、`simLabel` 与 `simSource`,用于判断收件卡槽。
|
| 配置项 | 默认值 | 说明 |
|
||||||
也可以显式按卡槽查询:
|
| --- | --- | --- |
|
||||||
|
| `serial.autoDetect` | `true` | 是否自动探测可响应 `AT` 的串口。 |
|
||||||
|
| `serial.probeTimeout` | `1200` | 单个串口探测超时,单位毫秒。 |
|
||||||
|
| `mobileData.cid` | `1` | 移动数据 PDP CID。有效范围 `1-15`,不要使用 `8`。 |
|
||||||
|
| `simCards` | `[]` | 只影响 Web 显示的 SIM 手机号或标签,不影响实际收发。`slot=0` 是 SIM1,`slot=1` 是 SIM2。 |
|
||||||
|
| `smtp` | 未启用 | 配置后会额外发送邮件通知。填写 `smtp.server` 后需要同时正确填写 `port`、`user`、`pass`、`sendTo`。 |
|
||||||
|
| `inbox.maxReceivedMessages` | `200` | 收件箱最多保留条数,代码上限是 `5000`。 |
|
||||||
|
| `inbox.receivedMessagesFile` | `data/received-messages.json` | 收件箱持久化文件。相对路径按项目根目录解析。 |
|
||||||
|
| `inbox.partitionBySim` | `true` | 查询收件箱时是否按 SIM 信息隔离。 |
|
||||||
|
| `inbox.allowAllSimMessages` | `false` | 是否允许显式查询全部 SIM 历史。 |
|
||||||
|
| `inbox.dualSimDefaultScope` | `all` | 双卡场景下默认收件箱范围,可选 `all` 或 `current`。 |
|
||||||
|
| `pushChannels` | `[]` | 推送通道列表。通道 `enabled=true` 时必须填写该类型要求的目标字段。 |
|
||||||
|
|
||||||
```bash
|
`api.webToken` 支持以下传入方式:
|
||||||
GET /api/sms/received?limit=50&simSlot=0 # SIM1
|
|
||||||
GET /api/sms/received?limit=50&simSlot=1 # SIM2
|
|
||||||
```
|
|
||||||
|
|
||||||
直出模式下,新短信主要通过模组 `+CMT` URC 进入收件箱;如果 URC 未携带卡槽信息,会显示为未知 SIM。
|
- `Authorization: Bearer <token>`
|
||||||
|
- `X-Web-Token: <token>`
|
||||||
|
- `?token=<token>`
|
||||||
|
- Web 管理台登录页
|
||||||
|
|
||||||
### 查询信号强度
|
## 推送通道
|
||||||
```bash
|
|
||||||
GET /api/modem/signal
|
|
||||||
```
|
|
||||||
|
|
||||||
### 查询模组信息
|
支持的 `pushChannels[].type`:
|
||||||
```bash
|
|
||||||
GET /api/modem/info
|
|
||||||
```
|
|
||||||
|
|
||||||
### 查询与切换SIM
|
| 类型 | 必填字段 |
|
||||||
```bash
|
| --- | --- |
|
||||||
GET /api/modem/sim
|
| `dingtalk` | `url`,`secret` 可选 |
|
||||||
POST /api/modem/sim/switch
|
| `feishu` | `url`,`secret` 可选 |
|
||||||
Content-Type: application/json
|
| `telegram` | `url` 填 Bot Token,`key1` 填 Chat ID |
|
||||||
|
| `pushplus` | `key1` 填 PushPlus Token |
|
||||||
|
| `serverchan` | `key1` 填 Server 酱 SendKey |
|
||||||
|
| `bark` | `url` |
|
||||||
|
| `post_json` | `url` |
|
||||||
|
| `get` | `url` |
|
||||||
|
| `custom` | `url`,`customBody` 可选 |
|
||||||
|
|
||||||
{
|
`customBody` 必须是合法 JSON 字符串,支持以下占位符:
|
||||||
"slot": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
当模组返回 `AT+DUALSIM?`、`AT+SWITCHSIM?` 与 `AT+BINDSIM?` 能力时,管理台会显示 SIM1/SIM2。短信接收使用 `CNMI` 直出模式,不为了接收短信切换 `SWITCHSIM`。如果模组上报的 URC 未携带卡槽信息,新短信会记录为未知 SIM。
|
- `{sender}`
|
||||||
|
- `{message}`
|
||||||
|
- `{timestamp}`
|
||||||
|
|
||||||
管理台切卡会同时设置 `AT+SWITCHSIM` 与 `AT+BINDSIM`,随后恢复短信直出配置。发送短信前会提示当前使用的 SIM。
|
`enabled=false` 的通道可以保留空字段;`enabled=true` 时会校验目标字段。
|
||||||
|
|
||||||
如果希望管理台显示手机号,可在 `config.json` 中配置:
|
## SIM 行为
|
||||||
|
|
||||||
|
启动初始化时程序会探测 SIM 状态。运行期间的普通状态刷新不会为了探测卡槽而持续切换 SIM。
|
||||||
|
|
||||||
|
发送短信时使用当前绑定的发送 SIM。Web 管理台的切卡入口放在“发送短信”区域,切换会同时设置模组的 `SWITCHSIM` 和 `BINDSIM`,然后恢复短信直出配置。
|
||||||
|
|
||||||
|
短信接收使用 `CNMI` 直出模式。收到短信时,如果模组 URC 携带卡槽信息,会记录为 SIM1 或 SIM2;如果 URC 没有卡槽信息,收件箱会显示为未知 SIM。
|
||||||
|
|
||||||
|
如果只想给管理台显示手机号,可配置:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -216,89 +191,193 @@ Content-Type: application/json
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发送AT命令
|
## HTTP API
|
||||||
|
|
||||||
|
所有 `/api/*` 接口都需要 token。下面示例使用 `X-Web-Token`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
POST /api/modem/at
|
TOKEN=change-this-token
|
||||||
Content-Type: application/json
|
BASE=http://localhost:3000
|
||||||
|
|
||||||
{
|
|
||||||
"command": "AT+CSQ"
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 获取日志
|
### 状态和日志
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
GET /api/logs
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/status"
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/modem/info"
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/modem/signal"
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/logs"
|
||||||
```
|
```
|
||||||
|
|
||||||
所有API均需要有效 token。可以通过 `Authorization: Bearer <token>`、`X-Web-Token` 请求头或 `token` 查询参数传入。
|
### 收件箱
|
||||||
|
|
||||||
## 项目结构
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/sms/received?limit=50"
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/sms/received?limit=50&scope=current"
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/sms/received?limit=50&simSlot=0"
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/sms/received?limit=50&simSlot=1"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 发送短信
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "$BASE/api/sms/send" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"phone":"13800138000","message":"测试短信"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### SIM 状态和切换
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/modem/sim"
|
||||||
|
|
||||||
|
curl -X POST "$BASE/api/modem/sim/switch" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"slot":1}'
|
||||||
|
```
|
||||||
|
|
||||||
|
`slot=0` 表示 SIM1,`slot=1` 表示 SIM2。
|
||||||
|
|
||||||
|
### 移动数据
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/modem/mobile-data"
|
||||||
|
|
||||||
|
curl -X POST "$BASE/api/modem/mobile-data" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"enabled":false}'
|
||||||
|
|
||||||
|
curl -X POST "$BASE/api/modem/mobile-data/consume" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"target":"8.8.8.8"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### AT 命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "$BASE/api/modem/at" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"command":"AT+CSQ"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 推送通道管理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Web-Token: $TOKEN" "$BASE/api/push/channels"
|
||||||
|
|
||||||
|
curl -X PUT "$BASE/api/push/channels" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"channels":[]}'
|
||||||
|
|
||||||
|
curl -X POST "$BASE/api/push/test" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Web-Token: $TOKEN" \
|
||||||
|
-d '{"enabled":true,"type":"post_json","name":"测试","url":"https://example.com/webhook"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
.
|
.
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── index.js # 入口文件
|
│ ├── index.js # 程序入口
|
||||||
│ ├── modem.js # 4G模块通信
|
│ ├── modem.js # 串口、AT 指令、SIM 和移动数据控制
|
||||||
│ ├── sms.js # 短信处理
|
│ ├── sms.js # PDU 解析、长短信处理后的转发、收件箱记录
|
||||||
│ ├── push.js # 推送通道
|
│ ├── concat.js # 长短信分段合并
|
||||||
│ ├── concat.js # 长短信合并
|
│ ├── push.js # 推送通道
|
||||||
│ ├── api.js # REST API
|
│ ├── api.js # Web 管理台和 HTTP API
|
||||||
│ └── logger.js # 日志系统
|
│ └── logger.js # 日志
|
||||||
├── config.json # 配置文件
|
├── public/ # Web 管理台静态文件
|
||||||
├── package.json
|
├── data/ # 收件箱持久化数据
|
||||||
└── README.md
|
├── logs/ # 运行日志
|
||||||
|
├── config.example.json
|
||||||
|
├── config.json # 本地配置,不应提交
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── Dockerfile
|
||||||
|
└── ecosystem.config.js
|
||||||
```
|
```
|
||||||
|
|
||||||
## 推送通道类型
|
## 常见问题
|
||||||
|
|
||||||
| 类型 | type值 | 配置项 |
|
|
||||||
|------|--------|--------|
|
|
||||||
| 钉钉机器人 | `dingtalk` | url, secret(可选) |
|
|
||||||
| 飞书机器人 | `feishu` | url, secret(可选) |
|
|
||||||
| Telegram Bot | `telegram` | url(bot token), key1(chat_id) |
|
|
||||||
| PushPlus | `pushplus` | key1(token) |
|
|
||||||
| Server酱 | `serverchan` | key1(sendkey) |
|
|
||||||
| Bark | `bark` | url(bark服务器) |
|
|
||||||
| POST JSON | `post_json` | url |
|
|
||||||
| GET请求 | `get` | url |
|
|
||||||
| 自定义模板 | `custom` | url, customBody |
|
|
||||||
|
|
||||||
`pushChannels` 中 `enabled: false` 的通道可以保留空字段;`enabled: true` 时必须配置对应目标:
|
|
||||||
|
|
||||||
- `post_json`、`bark`、`get`、`dingtalk`、`custom`、`feishu`:必须有 `url`。
|
|
||||||
- `telegram`:`url` 填 Bot Token,`key1` 填 Chat ID。
|
|
||||||
- `pushplus`:`key1` 填 PushPlus Token。
|
|
||||||
- `serverchan`:`key1` 填 Server酱 SendKey。
|
|
||||||
- `custom`:`customBody` 如果填写,必须是合法 JSON 字符串,支持 `{sender}`、`{message}`、`{timestamp}` 占位符。
|
|
||||||
|
|
||||||
## 长短信处理
|
|
||||||
|
|
||||||
- 自动检测长短信(分段数 > 1)
|
|
||||||
- 使用参考号和发送者号码标识同一条长短信
|
|
||||||
- 收齐所有分段后自动合并
|
|
||||||
- 30秒超时保护(未收齐也转发)
|
|
||||||
|
|
||||||
## 故障排查
|
|
||||||
|
|
||||||
### 串口打不开
|
### 串口打不开
|
||||||
|
|
||||||
|
Linux 下先确认设备存在:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Linux检查串口设备
|
|
||||||
ls -l /dev/ttyUSB*
|
ls -l /dev/ttyUSB*
|
||||||
# 添加用户到dialout组
|
|
||||||
sudo usermod -a -G dialout $USER
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 模块不响应
|
如果是权限问题,把运行用户加入串口权限组,重新登录后生效:
|
||||||
- 检查串口路径和波特率
|
|
||||||
- 检查USB连接
|
```bash
|
||||||
- 检查SIM卡是否插好
|
sudo usermod -a -G dialout "$USER"
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker 场景下确认 `docker-compose.yml` 已挂载 `/dev`,并保留了 `device_cgroup_rules`。
|
||||||
|
|
||||||
|
### 自动探测不到模组
|
||||||
|
|
||||||
|
- 确认 USB 连接和供电。
|
||||||
|
- 确认 `serial.baudRate` 正确。
|
||||||
|
- 临时关闭自动探测,把 `serial.autoDetect` 设为 `false`,并把 `serial.path` 写成真实 AT 串口。
|
||||||
|
- 查看日志中每个候选串口的探测结果。
|
||||||
|
|
||||||
|
### ML307A 首次上电被识别成网卡
|
||||||
|
|
||||||
|
ML307A 首次上电或恢复默认 USB 模式时,系统可能先把它识别为 RNDIS 网卡,而不是 USB 串口。此时 `lsusb` 能看到设备,但 `/dev/ttyUSB*` 里找不到可用 AT 串口。
|
||||||
|
|
||||||
|
可以用下面命令确认 USB 设备和内核识别结果:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lsusb
|
||||||
|
dmesg | grep -E 'ML307A|2ecc|3012|rndis|ttyUSB'
|
||||||
|
```
|
||||||
|
|
||||||
|
典型日志会包含类似内容:
|
||||||
|
|
||||||
|
```text
|
||||||
|
usb 1-3: New USB device found, idVendor=2ecc, idProduct=3012
|
||||||
|
usb 1-3: Product: ML307A
|
||||||
|
usb 1-3: Manufacturer: CMIOT
|
||||||
|
rndis_host 1-3:1.0: rndis media connect
|
||||||
|
rndis_host 1-3:1.0 enxac0c29a39b6d: renamed from eth0
|
||||||
|
```
|
||||||
|
|
||||||
|
如果出现上述 RNDIS 网卡日志,并且系统没有生成 USB 串口,可以把该 USB ID 添加到 `option` 串口驱动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo modprobe option
|
||||||
|
echo 2ecc 3012 | sudo tee /sys/bus/usb-serial/drivers/option1/new_id
|
||||||
|
```
|
||||||
|
|
||||||
|
执行后重新查看 `/dev/ttyUSB*`,再把 `config.json` 里的 `serial.path` 指向实际 AT 串口。不同内核、发行版或模组固件的枚举行为可能不完全一致;如果后续仍会被识别为网卡,可按系统发行版方式配置 udev/modprobe 规则。
|
||||||
|
|
||||||
### 收不到短信
|
### 收不到短信
|
||||||
- 检查AT+CEREG?网络注册状态
|
|
||||||
- 检查AT+CNMI配置是否正确
|
|
||||||
- 检查SIM卡余额和服务状态
|
|
||||||
|
|
||||||
## 许可证
|
- 确认 SIM 卡已插好且可以注册网络。
|
||||||
|
- 查看 `/api/status` 里的 `ready`、`operator`、`signal`。
|
||||||
|
- 确认日志里有 `PDU模式设置完成` 和 `CNMI参数设置完成`。
|
||||||
|
- 部分模组不会在 URC 里带卡槽信息,这只影响收件箱显示的 SIM 归属,不代表短信没有收到。
|
||||||
|
|
||||||
|
### 发送短信失败
|
||||||
|
|
||||||
|
- 先在管理台确认当前发送 SIM。
|
||||||
|
- 确认目标号码格式正确。
|
||||||
|
- 查看日志里的 AT 响应。
|
||||||
|
- 如果刚切换 SIM,等待模组重新注册网络后再发。
|
||||||
|
|
||||||
|
## 安全说明
|
||||||
|
|
||||||
|
- 不要提交 `config.json`、token、手机号、Webhook URL、SMTP 密码和日志。
|
||||||
|
- 管理台和 API 只使用 `api.webToken` 鉴权。
|
||||||
|
- `POST /api/modem/at` 可以执行任意 AT 命令,只应暴露在可信网络内。
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|||||||
Reference in New Issue
Block a user