feat: add mock-bind endpoint to bypass token flow for testing

The bind + confirm two-step flow has timezone issues between Node.js
(UTC on SCF) and MySQL (timezone +08:00) that cause bind_token to
fail validation. Add POST /api/v1/device/mock-bind that does bind +
confirm in one atomic step, skipping the token entirely. Only
available when NODE_ENV != production.

Update ble-connect and index page mock buttons to use mock-bind.
这个提交包含在:
Guoguo
2026-04-29 05:10:12 -07:00
父节点 3e8cd8764a
当前提交 775112130d
修改 3 个文件,包含 35 行新增25 行删除
+25
查看文件
@@ -62,6 +62,31 @@ function register(router) {
return ok({ message: 'success', subscription: sub ? { plan: sub.plan, remaining_days: sub.remaining_days } : { plan: 'none', remaining_days: 0 } })
})
router.post('/api/v1/device/mock-bind', async ctx => {
const config = require('../config')
if (config.nodeEnv === 'production') return fail(2001, 'not available in production')
const user = await requireUser(ctx)
if (!user) return fail(1001, 'invalid_token')
const deviceId = String(ctx.body.device_id || '').trim()
if (!deviceId) return fail(2001, 'device_id required')
const result = await transaction(async conn => {
const [active] = await conn.execute('SELECT device_id FROM bindings WHERE user_id = ? AND bind_status = 1 LIMIT 1', [user.user_id])
if (active.length > 0) return { duplicated: true, device_id: active[0].device_id }
const [devices] = await conn.execute('SELECT * FROM devices WHERE device_id = ? AND status <> 4 LIMIT 1', [deviceId])
if (devices.length === 0) return { invalid: true }
await conn.execute('UPDATE bindings SET bind_status = 2 WHERE user_id = ? AND bind_status = 3', [user.user_id])
await conn.execute('INSERT INTO bindings (user_id, device_id, bind_token, bind_expires, bind_status, bind_time) VALUES (?, ?, ?, NOW(), 1, NOW())', [user.user_id, deviceId, 'mock'])
await ensureTrial(conn, user.user_id)
return { success: true }
})
if (result.invalid) return fail(1005, 'DEVICE_NOT_FOUND')
if (result.duplicated) return fail(2001, '已绑定设备', { device_id: result.device_id })
await writeLog({ user_id: user.user_id, action: 'device_bind_confirm', detail: '模拟绑定设备: ' + deviceId, ip: ctx.ip })
return ok({ message: 'success', device_id: deviceId })
})
router.post('/api/v1/device/unbind', async ctx => {
const user = await requireUser(ctx)
if (!user) return fail(1001, 'invalid_token')