From ee7b3053e2944bb9c43e762a4f71526b886c97ac Mon Sep 17 00:00:00 2001 From: posimai Date: Sat, 11 Apr 2026 06:42:42 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=88=9D=E5=9B=9E=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=83=86=E3=82=A3=E3=83=99=E3=83=BC=E3=83=88=E3=81=AE=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=82=B3=E3=83=B3=E3=83=87=E3=82=A3=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WHERE device_id IS NULL を追加してアトミックにし、 競合した場合は再取得して照合する Co-Authored-By: Claude Sonnet 4.6 --- routes/ponshu.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/routes/ponshu.js b/routes/ponshu.js index b83a2ed3..33bf9932 100644 --- a/routes/ponshu.js +++ b/routes/ponshu.js @@ -40,12 +40,28 @@ module.exports = function createPonshuRouter(pool, authMiddleware) { }); } - // 初回アクティベート + // 初回アクティベート — WHERE device_id IS NULL で競合を防ぐ if (!license.device_id) { - await pool.query( - `UPDATE ponshu_licenses SET device_id = $1, activated_at = NOW() WHERE license_key = $2`, + const activateResult = await pool.query( + `UPDATE ponshu_licenses SET device_id = $1, activated_at = NOW() + WHERE license_key = $2 AND device_id IS NULL + RETURNING license_key`, [device_id, license_key] ); + if (activateResult.rowCount === 0) { + // 別リクエストが先にアクティベートした → 再取得して照合 + const retry = await pool.query( + `SELECT device_id FROM ponshu_licenses WHERE license_key = $1`, [license_key] + ); + const current = retry.rows[0]; + if (!current || current.device_id !== device_id) { + return res.json({ + valid: false, + error: '別のデバイスで登録済みです。端末変更の場合はサポートまでご連絡ください。', + supportEmail: APP_SUPPORT_EMAIL, + }); + } + } console.log(`[Ponshu] License activated: ${license_key.substring(0, 12)}... -> Device: ${device_id.substring(0, 8)}...`); return res.json({ valid: true, plan: license.plan, activated: true }); }