# 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 API,JWT 鉴权,SQLite 存储。 - **Web 端**:React + Vite SPA,Material 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 `。 ### 认证 #### 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 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 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 - 点击行查看详情面板(右侧) - 每条短信可单独删除 ## 安全说明 - 密码经 bcrypt(10轮)哈希存储,不保存明文 - 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" ```