Files
transfer_sms/README.md
2026-04-28 17:34:03 +08:00

370 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SMS Monitor
一个基于 Android + Node.js 的个人短信监控系统。Android App 读取手机短信并上传至服务器Web Dashboard 提供网页端查看与管理。
## 项目架构
```
┌─────────────────┐ HTTP / JSON ┌─────────────────┐ SQL ┌──────────┐
│ Android App │ ────────────────────→ │ Node.js Server │ ──────────→ │ SQLite │
│ (Flutter) │ │ (Express) │ │ │
└─────────────────┘ └────────┬────────┘ └──────────┘
│ REST API
┌─────────────────┐
│ Web Dashboard │
│ (React + Vite) │
└─────────────────┘
```
- **App 端**Flutter 编写,仅支持 Android。读取系统短信数据库 + 实时监听新短信,支持自动/手动上传。
- **服务端**Node.js + Express 提供 REST APIJWT 鉴权SQLite 存储。
- **Web 端**React + Vite SPAMaterial You 风格,登录后查看/筛选/删除短信。
## 技术栈
| 层级 | 技术 | 说明 |
|------|------|------|
| Android App | Flutter 3.2+ | `dynamic_color` Material You 主题 |
| 短信读取 | `sms_maintained` | 查询系统短信数据库 + 接收新短信广播 |
| 权限管理 | `permission_handler` | Android SMS 权限 |
| 后端框架 | Express 4.x | TypeScript 编写 |
| 数据库 | better-sqlite3 | WAL 模式,同步 API |
| 鉴权 | JWT (jsonwebtoken) | 30 天有效期 |
| 密码加密 | bcryptjs | 10 轮哈希 |
| 前端框架 | React 18 + Vite 6 | TypeScript |
| HTTP 客户端 | fetch (内置) | 无额外依赖 |
## 目录结构
```
transfer_sms/
├── server/
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
│ ├── index.ts # Express 入口,挂载路由
│ ├── db.ts # SQLite 初始化 + 自动建表
│ ├── types.ts # TypeScript 类型定义
│ ├── middleware/
│ │ └── auth.ts # JWT Bearer Token 鉴权中间件
│ └── routes/
│ ├── auth.ts # POST /api/auth/register, /api/auth/login
│ ├── sms.ts # POST /api/sms/upload, GET /api/sms, DELETE /api/sms/:id
│ └── devices.ts # POST /api/devices/register
├── web/
│ ├── package.json
│ ├── vite.config.ts # 开发代理 /api → localhost:3000
│ └── src/
│ ├── main.tsx # 入口
│ ├── App.tsx # 路由:未登录 → Login已登录 → SmsList
│ ├── api.ts # 封装 fetch 调用
│ ├── index.css # Material You CSS 变量 + 基础样式
│ └── pages/
│ ├── Login.tsx / .css # 登录/注册页
│ └── SmsList.tsx / .css # 短信列表 + 详情面板 + 筛选
└── app/
├── pubspec.yaml
└── lib/
├── main.dart # 入口DynamicColorBuilder 主题
├── models/
│ └── sms_message.dart # SmsMessage 数据类 + JSON 序列化
├── services/
│ ├── api_service.dart # HTTP 客户端
│ ├── sms_reader_service.dart # 查询/监听系统短信
│ └── settings_service.dart # SharedPreferences 读写
└── screens/
├── login_screen.dart # 服务器地址 + 用户名密码登录
├── home_screen.dart # NavigationBar Tab 容器
├── sms_list_screen.dart # 短信列表 + 选择上传 + 自动模式
└── settings_screen.dart # 服务器信息 + 上传模式 + 登出
```
## 快速开始
### 1. 启动服务端
```bash
cd server
npm install
npm run dev
# 服务运行在 http://localhost:3000
```
### 2. 启动 Web Dashboard
```bash
cd web
npm install
npm run dev
# 访问 http://localhost:5173
```
Vite 开发服务器已配置 API 代理,`/api/*` 请求会自动转发到 `localhost:3000`
### 3. 运行 Flutter App
```bash
cd app
flutter pub get
flutter run
```
需要 Android 设备或模拟器,且需要安装 Flutter SDK。
## API 文档
所有 API 均返回 JSON。需要鉴权的接口在 Header 中携带 `Authorization: Bearer <token>`
### 认证
#### POST /api/auth/register
注册新用户,成功后直接返回 JWT token。
```
Request:
{
"username": "alice", // 3-50 字符
"password": "123456" // 6-100 字符
}
Response 201:
{
"token": "eyJhbG...",
"userId": 1
}
Error 409: { "error": "Username already taken" }
Error 400: { "error": [...] } // 参数校验失败
```
#### POST /api/auth/login
登录已有用户。
```
Request:
{
"username": "alice",
"password": "123456"
}
Response 200:
{
"token": "eyJhbG...",
"userId": 1
}
Error 401: { "error": "Invalid credentials" }
```
### 短信
#### POST /api/sms/upload
批量上传短信,单次不超过 5MB。
```
Headers: Authorization: Bearer <token>
Request:
[
{
"phone_number": "+8613800138000",
"contact_name": "张三", // 可选
"content": "你好,明天见面聊",
"type": "received", // "received" | "sent"
"sms_date": "2026-04-28T10:30:00.000Z" // ISO 8601
}
]
Response 200:
{
"uploaded": 1 // 成功插入条数
}
```
#### GET /api/sms
分页查询当前用户的短信。
```
Headers: Authorization: Bearer <token>
Query:
page - 页码,默认 1
limit - 每页条数,默认 20最大 100
phone - 按手机号模糊搜索(可选)
type - 按类型筛选,"received" | "sent"(可选)
Response 200:
{
"messages": [ ... ],
"total": 42,
"page": 1,
"limit": 20,
"totalPages": 3
}
```
#### GET /api/sms/:id
查看单条短信详情。
```
Response 200:
{
"id": 1,
"user_id": 1,
"phone_number": "+8613800138000",
"contact_name": "张三",
"content": "你好,明天见面聊",
"type": "received",
"sms_date": "2026-04-28T10:30:00.000Z",
"created_at": "2026-04-28 12:00:00"
}
Error 404: { "error": "Not found" }
```
#### DELETE /api/sms/:id
删除一条短信(仅限自己的)。
```
Response 200: { "deleted": true }
Error 404: { "error": "Not found" }
```
### 设备
#### POST /api/devices/register
注册设备,重复注册同名设备会更新同步时间。
```
Request:
{
"device_name": "Pixel 8 Pro"
}
Response 201: { "deviceId": 1 }
```
## 数据库
使用 SQLite数据库文件自动创建在 `server/data.db`
```sql
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE sms_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
phone_number TEXT NOT NULL,
contact_name TEXT,
content TEXT NOT NULL,
type TEXT NOT NULL CHECK(type IN ('received', 'sent')),
sms_date DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_sms_user ON sms_messages(user_id, sms_date);
CREATE TABLE devices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
device_name TEXT NOT NULL,
last_sync_at DATETIME
);
```
## App 功能说明
### 登录
- 首次使用需填写服务器地址、用户名和密码
- 支持注册新用户或登录已有账号
- 配置自动保存至 SharedPreferences
### 短信列表
- 读取手机所有短信(收件箱 + 已发送)
- 每条短信显示联系人、内容摘要、类型箭头图标
- 多选模式:点击短信或圆圈图标选中,选中的显示对号
- 点击云上传按钮上传已选中的短信
- 点击右下角悬浮按钮一键上传全部短信
### 上传模式
- **手动模式**(默认):用户选择短信后点击上传
- **自动模式**:开启后实时监听新短信,自动上传到服务器
- 自动模式下新的入站短信会立即出现在列表中
### 设置
- 查看当前服务器地址和 Token 状态
- 切换自动/手动上传模式
- 登出按钮清除 Token 并返回登录页
## Web Dashboard 功能说明
### 登录页
- 用户名 + 密码登录或注册
- Material You 风格卡片布局
### 短信列表页
- 表格展示所有上传的短信
- 按手机号码模糊搜索
- 按类型(发送/接收)下拉筛选
- 分页浏览,支持 Prev/Next
- 点击行查看详情面板(右侧)
- 每条短信可单独删除
## 安全说明
- 密码经 bcrypt10轮哈希存储不保存明文
- JWT token 有效期 30 天
- 所有短信接口均需认证,用户只能访问自己的数据
- 生产环境请修改 `JWT_SECRET` 环境变量
```bash
JWT_SECRET=your-secret-key npm run dev
```
## 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `PORT` | 3000 | 服务端口 |
| `JWT_SECRET` | 内置默认值 | JWT 签名密钥,生产环境必须修改 |
## 验证测试
```bash
# 注册用户
curl -s -X POST http://localhost:3000/api/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456"}'
# 登录获取 token
TOKEN=$(curl -s -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456"}' | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
# 上传短信
curl -s -X POST http://localhost:3000/api/sms/upload \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '[{"phone_number":"+8613800138000","contact_name":"张三","content":"你好","type":"received","sms_date":"2026-04-28T10:30:00.000Z"}]'
# 查询短信列表
curl -s http://localhost:3000/api/sms -H "Authorization: Bearer $TOKEN"
# 删除
curl -s -X DELETE http://localhost:3000/api/sms/1 -H "Authorization: Bearer $TOKEN"
```