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
这个提交包含在:
+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 })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户