feat: implement P0 security and reliability improvements

- bcrypt password hashing with auto-migration from SHA-256
- BLE command retry (3 attempts, 500ms delay, skip on disconnect)
- BLE auto-reconnect with service re-discovery on disconnect
- Treatment page disconnect/reconnect event handling
- Token refresh endpoint with 3-day grace period
- Client-side token auto-refresh when <24h remaining
- Single treatment record detail API with ownership check
这个提交包含在:
Guoguo
2026-04-28 18:12:30 -07:00
父节点 88adee7743
当前提交 91d5937d8e
修改 10 个文件,包含 307 行新增32 行删除
+40 -2
查看文件
@@ -1,8 +1,10 @@
const jwt = require('jsonwebtoken')
const { one, query } = require('../lib/db')
const { ok, fail } = require('../lib/response')
const { signUser } = require('../lib/auth')
const { signUser, readBearer } = require('../lib/auth')
const { code2Session } = require('../lib/wechat')
const { writeLog } = require('../lib/log')
const config = require('../config')
function register(router) {
router.post('/api/v1/auth/login', async ctx => {
@@ -33,7 +35,43 @@ function register(router) {
})
router.post('/api/v1/auth/refresh', async ctx => {
return fail(2001, 'refresh_token 暂未启用,请重新登录')
const token = readBearer(ctx.headers)
if (!token) return fail(1001, 'token_expired')
let payload
try {
payload = jwt.verify(token, config.jwt.secret)
} catch (err) {
if (err.name === 'TokenExpiredError') {
try {
payload = jwt.verify(token, config.jwt.secret, { ignoreExpiration: true })
} catch (_) {
return fail(1001, 'token_expired')
}
const now = Math.floor(Date.now() / 1000)
const gracePeriod = 3 * 24 * 60 * 60
if (now - payload.exp > gracePeriod) {
return fail(1001, 'token_expired')
}
} else {
return fail(1001, 'token_expired')
}
}
if (payload.type !== 'user') return fail(1001, 'token_expired')
// Check if token is within 7 days of expiry (for non-expired tokens)
const now = Math.floor(Date.now() / 1000)
const sevenDays = 7 * 24 * 60 * 60
if (payload.exp && payload.exp > now && (payload.exp - now) > sevenDays) {
return ok({ token, expires_in: payload.exp - now })
}
const user = await one('SELECT * FROM users WHERE user_id = :user_id AND status = 1', { user_id: payload.user_id })
if (!user) return fail(1001, 'token_expired')
const newToken = signUser(user)
return ok({ token: newToken, expires_in: 604800 })
})
}