docs: update AGENTS.md with current architecture and security measures
这个提交包含在:
@@ -1,74 +1,128 @@
|
|||||||
# Repository Reality
|
# Repository Reality
|
||||||
|
|
||||||
- This repository now contains the WeChat Mini Program, the uniapp/Vue 3 admin console, a Tencent Cloud SCF HTTP backend, deployment docs, and design/planning references.
|
- WeChat Mini Program (`miniprogram/`) + Tencent Cloud SCF HTTP backend (`server/`) + Vue 3 H5 admin console (`admin-console/`).
|
||||||
- Current backend implementation lives in `server/` and targets Tencent Cloud HTTP Function + TencentDB MySQL + COS.
|
- Backend: Express.js with DAO layer, deployed as Tencent Cloud HTTP Function.
|
||||||
- The previous WeChat Cloud Development function directories have been removed from the active architecture.
|
- Database: TencentDB MySQL, schema in `server/sql/schema.sql`.
|
||||||
- Do not treat older planning/review documents as current implementation truth unless they explicitly say they are current.
|
- Storage: Tencent COS for firmware and admin H5 artifacts.
|
||||||
|
|
||||||
# Verified System Scope
|
# Architecture
|
||||||
|
|
||||||
- Mini Program: native WeChat `WXML` / `WXSS` / `JS` under `miniprogram/`.
|
## Server (`server/src/`)
|
||||||
- Device communication: BLE 5.0 GATT via `miniprogram/services/ble.js`.
|
|
||||||
- Mini Program cloud API: HTTPS requests through `miniprogram/utils/request.js` to the configured API base in `miniprogram/config/env.js`.
|
|
||||||
- Backend: Tencent Cloud HTTP Function, package root uses executable `server/scf_bootstrap` to start `node scripts/local-server.js` on port `9000`.
|
|
||||||
- Database: TencentDB MySQL schema in `server/sql/schema.sql`.
|
|
||||||
- Storage: Tencent COS for firmware/admin H5 artifacts.
|
|
||||||
- Admin console: uniapp + Vue 3 under `admin-console/`, H5 build can be uploaded to COS.
|
|
||||||
|
|
||||||
# Current Deployment Facts
|
```
|
||||||
|
app.js — Express app, middleware, route mounting, rate limiting
|
||||||
- Test API base is configured as `https://1426323813-ilxkhlxf4p.ap-guangzhou.tencentscf.com` in both Mini Program and admin console test configs.
|
index.js — SCF entry point (lib/serverless.js adapter)
|
||||||
- SCF function name used during deployment: `jw-beauty-api`.
|
config.js — Environment config with production guards
|
||||||
- SCF is an HTTP function, not an event function. Use `scf_bootstrap`, not `index.main_handler`, for this function type.
|
lib/
|
||||||
- SCF must be attached to the TencentDB VPC/subnet when using the database private endpoint.
|
db.js — MySQL2 pool, query/one/transaction helpers, namedPlaceholders
|
||||||
- Real secrets must remain in local `.env` or SCF environment variables. Never commit `server/.env`.
|
auth.js — JWT sign/verify, bcrypt compare, randomHex
|
||||||
|
response.js — ok()/fail() response wrappers
|
||||||
# Integration Facts Worth Preserving
|
settings-cache.js — Cached settings with 60s TTL + invalidateCache()
|
||||||
|
serverless.js — SCF event → Express req/res adapter
|
||||||
- BLE frame format implemented in code: header `0xAA 0x55`, length, type, payload, XOR checksum over header/length/type/payload.
|
utils.js — toMysqlDate (UTC+8 aware)
|
||||||
- BLE services in the document/code: `FFE0` device info, `FFE1` data communication, `FFE2` OTA.
|
middleware/
|
||||||
- Device binding is two-phase: backend bind request creates pending token, Mini Program sends BLE bind, device returns bind success, Mini Program confirms backend binding.
|
auth.js — requireUser/requireAdmin Express middleware
|
||||||
- Device commands are queued in MySQL `device_commands`; Mini Program pulls pending commands and executes BLE commands.
|
dao/ — 11 DAO files: admin, binding, command, device, device-event,
|
||||||
- Current main cloud path is HTTPS API through the Mini Program, not MQTT/IoT direct device-cloud communication.
|
firmware, log, settings, subscription, treatment, user
|
||||||
|
routes/ — 7 route files: admin, auth, device, firmware, subscription, treatment, user
|
||||||
# User Profile / Login Reality
|
scripts/
|
||||||
|
local-server.js — Local dev server (app.listen)
|
||||||
- `wx.login()` only provides a code for backend `openid/session_key` exchange.
|
init-db.js — Database schema initialization
|
||||||
- The current login page uses `wx.getUserProfile()` to request WeChat's official profile authorization popup and then stores returned nickname/avatar if WeChat provides them.
|
|
||||||
- WeChat may still return masked values such as `微信用户` and default avatar. This is platform policy, not a backend bug.
|
|
||||||
- If reliable nickname/avatar capture becomes required, use the newer `chooseAvatar` + `input type="nickname"` flow, but that is not the current requested UX.
|
|
||||||
- Phone number authorization is scaffolded through `open-type="getPhoneNumber"` and backend `/api/v1/user/phone`, currently hidden in the login UI.
|
|
||||||
|
|
||||||
# Build And Deploy Commands
|
|
||||||
|
|
||||||
Backend local:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd server
|
|
||||||
npm install
|
|
||||||
npm run db:init
|
|
||||||
npm start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Admin H5:
|
**Route mounting:**
|
||||||
|
- `/api/v1` — auth, user, device, subscription, treatment, firmware
|
||||||
|
- `/api/v1/admin` — admin routes
|
||||||
|
- Rate limiting: user login 10/15min, admin login 5/15min
|
||||||
|
|
||||||
```bash
|
**Auth:**
|
||||||
cd admin-console
|
- User: JWT_SECRET, payload.type === 'user', 7d expiry, 1d refresh grace
|
||||||
npm install
|
- Admin: ADMIN_JWT_SECRET, payload.type === 'admin'
|
||||||
npm run build:h5
|
- bcrypt for passwords, SHA-256 legacy auto-migration
|
||||||
npm run deploy:cos
|
|
||||||
|
## Mini Program (`miniprogram/`)
|
||||||
|
|
||||||
|
```
|
||||||
|
pages/ — 15+ pages (index, profile, subscribe-*, treating, etc.)
|
||||||
|
services/
|
||||||
|
ble.js — Proxy: module.exports = require('./ble/index')
|
||||||
|
ble/
|
||||||
|
protocol.js — Constants, frame encode/decode, uint32ToBytes, hexToBytes
|
||||||
|
connection.js — Scan, connect, disconnect, reconnect, event emitter
|
||||||
|
commands.js — writeCommand, startTreatment, stopTreatment, bindDevice
|
||||||
|
index.js — Barrel export (39 exports including REGION_NAMES)
|
||||||
|
utils/
|
||||||
|
api.js — Named API functions matching server routes
|
||||||
|
request.js — wx.request wrapper with token refresh
|
||||||
|
page.js — getStatusBarHeight, navigateBack, isDevMode
|
||||||
|
config/
|
||||||
|
env.js — API base URL per environment
|
||||||
```
|
```
|
||||||
|
|
||||||
Mini Program BLE frame test:
|
**BLE frame format:** `0xAA 0x55 | length | type | payload | XOR checksum`
|
||||||
|
|
||||||
```bash
|
**Device binding:** Two-step (bind request → BLE handshake → confirm) or mock-bind (dev only).
|
||||||
cd miniprogram
|
|
||||||
node scripts/test-ble-frame.js
|
## Admin Console (`admin-console/`)
|
||||||
|
|
||||||
|
```
|
||||||
|
pages/
|
||||||
|
login/index.vue — Standalone login page
|
||||||
|
admin/index.vue — SPA shell: AdminLayout + keep-alive + dynamic <component :is>
|
||||||
|
views/ — 9 views: Dashboard, DeviceList, DeviceDetail, UserList,
|
||||||
|
UserDetail, Subscription, Record, Log, Settings
|
||||||
|
components/
|
||||||
|
AdminLayout.vue — Sidebar nav, emits 'navigate' with view names
|
||||||
|
DataTable.vue — Reusable table + pagination
|
||||||
|
ConfirmModal.vue — Reusable modal
|
||||||
|
utils/
|
||||||
|
useList.js — listMixin for paginated list views
|
||||||
|
styles/
|
||||||
|
common.css — Shared styles including form classes
|
||||||
```
|
```
|
||||||
|
|
||||||
# Working Rules For Future Sessions
|
**SPA routing:** Component-based (no URL routing). keep-alive caches views by `name` property.
|
||||||
|
|
||||||
- Use current executable config and code as the source of truth before relying on older planning docs.
|
# Key Design Decisions
|
||||||
- Do not reintroduce `wx.cloud`, WeChat Cloud Development functions, or MQTT unless the architecture is explicitly changed again.
|
|
||||||
- Do not commit `.env`, cloud credentials, database passwords, signed COS URLs, or generated build artifacts.
|
1. **Subscription extends, never overwrites** — purchase() adds days to existing expire_time via DATE_ADD
|
||||||
- Before committing, check `git status --short` and scan staged files for secret-looking values.
|
2. **Settings cache** — 60s TTL in-memory, invalidated on admin save
|
||||||
|
3. **Feature toggles** — maintenance_mode, enable_binding enforced server-side via settings-cache
|
||||||
|
4. **Timezone** — SCF runs UTC, MySQL connection timezone +08:00, toMysqlDate forces UTC+8
|
||||||
|
5. **Mock endpoints** — mock-bind, mock-purchase restricted to non-production (config.nodeEnv !== 'production')
|
||||||
|
6. **BLE module resolution** — `services/ble.js` proxy file exists because WeChat `require('./ble')` doesn't resolve `ble/index.js`
|
||||||
|
|
||||||
|
# Security Measures
|
||||||
|
|
||||||
|
- Rate limiting on auth endpoints (express-rate-limit)
|
||||||
|
- Production guards: throw if JWT secrets or admin credentials use defaults
|
||||||
|
- Parameterized queries (namedPlaceholders) everywhere except one Number()-coerced IN clause
|
||||||
|
- JWT type field prevents cross-contamination between user/admin tokens
|
||||||
|
- CORS currently `*` (development phase, to be restricted for production)
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
|
||||||
|
**Backend local dev:**
|
||||||
|
```bash
|
||||||
|
cd server && npm install && npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
**SCF deploy:**
|
||||||
|
- HTTP function, uses `scf_bootstrap` (not event handler)
|
||||||
|
- Must be in TencentDB VPC/subnet
|
||||||
|
- Function name: `jw-beauty-api`
|
||||||
|
|
||||||
|
**Admin H5:**
|
||||||
|
```bash
|
||||||
|
cd admin-console && npm install && npm run build:h5 && npm run deploy:cos
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test API base:** `https://1426323813-ilxkhlxf4p.ap-guangzhou.tencentscf.com`
|
||||||
|
|
||||||
|
# Working Rules
|
||||||
|
|
||||||
|
- Use current code as source of truth, not older planning docs
|
||||||
|
- Never commit `.env`, credentials, or signed URLs
|
||||||
|
- WeChat miniprogram `require()` does not auto-resolve directories — always use explicit proxy files
|
||||||
|
- `wx.getUserProfile()` is deprecated; if reliable profile needed, use `chooseAvatar` + nickname input
|
||||||
|
- Mock/dev endpoints are gated by `config.nodeEnv !== 'production'`
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户