2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 22:04:24 +08:00
2026-04-28 17:34:03 +08:00
2026-04-28 17:34:03 +08:00
2026-04-28 17:34:03 +08:00

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. 启动服务端

cd server
npm install
npm run dev
# 服务运行在 http://localhost:3000

2. 启动 Web Dashboard

cd web
npm install
npm run dev
# 访问 http://localhost:5173

Vite 开发服务器已配置 API 代理,/api/* 请求会自动转发到 localhost:3000

3. 运行 Flutter App

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

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 环境变量
JWT_SECRET=your-secret-key npm run dev

环境变量

变量 默认值 说明
PORT 3000 服务端口
JWT_SECRET 内置默认值 JWT 签名密钥,生产环境必须修改

验证测试

# 注册用户
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"
Description
No description provided
Readme 434 KiB
Languages
HTML 56.7%
Dart 11.6%
C++ 10.5%
CMake 8.1%
TypeScript 7%
Other 6%