feat(activate): allow retireDeviceId to free a device slot on migration
Supports client migration from per-user UUID to machine fingerprint without consuming an extra maxDevices slot. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+13
-4
@@ -175,6 +175,8 @@ export function createServer(options = {}) {
|
||||
const body = JSON.parse(raw || '{}');
|
||||
const productKey = body.productKey;
|
||||
const deviceId = body.deviceId;
|
||||
const retireDeviceId =
|
||||
typeof body.retireDeviceId === 'string' ? body.retireDeviceId.trim() : '';
|
||||
if (!productKey || !deviceId) return json(res, 400, { error: 'productKey_and_deviceId_required' });
|
||||
|
||||
const data = readData();
|
||||
@@ -184,8 +186,13 @@ export function createServer(options = {}) {
|
||||
|
||||
data.activations ??= {};
|
||||
const list = data.activations[pk.sub] ?? [];
|
||||
const already = list.includes(deviceId);
|
||||
if (!already && list.length >= pk.maxDevices) {
|
||||
// Миграция со старого per-user UUID: освобождаем слот retireDeviceId перед проверкой лимита.
|
||||
let next = [...list];
|
||||
if (retireDeviceId && retireDeviceId !== deviceId) {
|
||||
next = next.filter((d) => d !== retireDeviceId);
|
||||
}
|
||||
const already = next.includes(deviceId);
|
||||
if (!already && next.length >= pk.maxDevices) {
|
||||
return json(res, 403, { error: 'too_many_devices' });
|
||||
}
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
@@ -199,8 +206,10 @@ export function createServer(options = {}) {
|
||||
}
|
||||
|
||||
if (!already) {
|
||||
list.push(deviceId);
|
||||
data.activations[pk.sub] = list;
|
||||
next.push(deviceId);
|
||||
}
|
||||
if (next.length !== list.length || next.some((d, i) => d !== list[i])) {
|
||||
data.activations[pk.sub] = next;
|
||||
dataChanged = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user