Add period-based license keys alongside fixed expiry dates.
New keys default to validDays with countdown starting on first activation; existing expiresAtSec keys behave unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+35
-15
@@ -4,6 +4,12 @@ import http from 'node:http';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import {
|
||||
enrichLicenseEntry,
|
||||
ensurePeriodActivated,
|
||||
parseProductKeyExpiry,
|
||||
resolveTokenExp,
|
||||
} from '../lib/licenseLogic.mjs';
|
||||
import { signPayload } from '../lib/signPayload.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -104,19 +110,37 @@ export function createServer(options = {}) {
|
||||
if (!already && list.length >= pk.maxDevices) {
|
||||
return json(res, 403, { error: 'too_many_devices' });
|
||||
}
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
let dataChanged = false;
|
||||
|
||||
try {
|
||||
const period = ensurePeriodActivated(pk, now);
|
||||
if (period.changed) dataChanged = true;
|
||||
} catch {
|
||||
return json(res, 403, { error: 'license_misconfigured' });
|
||||
}
|
||||
|
||||
if (!already) {
|
||||
list.push(deviceId);
|
||||
data.activations[pk.sub] = list;
|
||||
writeData(data);
|
||||
dataChanged = true;
|
||||
}
|
||||
|
||||
if (dataChanged) writeData(data);
|
||||
|
||||
let exp;
|
||||
try {
|
||||
exp = resolveTokenExp(pk, now);
|
||||
} catch {
|
||||
return json(res, 403, { error: 'license_misconfigured' });
|
||||
}
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const payload = {
|
||||
v: 1,
|
||||
sub: pk.sub,
|
||||
pid: pk.pid,
|
||||
iat: now,
|
||||
exp: pk.expiresAtSec,
|
||||
exp,
|
||||
did: deviceId,
|
||||
};
|
||||
const token = signPayload(payload, privateKeyPem);
|
||||
@@ -131,16 +155,8 @@ export function createServer(options = {}) {
|
||||
const activations = data.activations ?? {};
|
||||
const licenses = (data.productKeys ?? []).map((pk) => {
|
||||
const devices = activations[pk.sub] ?? [];
|
||||
return {
|
||||
key: pk.key,
|
||||
sub: pk.sub,
|
||||
pid: pk.pid,
|
||||
maxDevices: pk.maxDevices,
|
||||
expiresAtSec: pk.expiresAtSec,
|
||||
revoked: revokedKeysList.includes(pk.key) || revokedSubsList.includes(pk.sub),
|
||||
activatedDevices: devices,
|
||||
activatedCount: devices.length,
|
||||
};
|
||||
const revoked = revokedKeysList.includes(pk.key) || revokedSubsList.includes(pk.sub);
|
||||
return enrichLicenseEntry(pk, devices, revoked);
|
||||
});
|
||||
return json(res, 200, { licenses });
|
||||
}
|
||||
@@ -149,13 +165,17 @@ export function createServer(options = {}) {
|
||||
if (!checkAdmin(req, adminToken)) return json(res, 401, { error: 'unauthorized' });
|
||||
const raw = await readBody(req);
|
||||
const body = JSON.parse(raw || '{}');
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const expiry = parseProductKeyExpiry(body);
|
||||
if (!expiry.ok) return json(res, 400, { error: expiry.error });
|
||||
|
||||
const entry = {
|
||||
key: body.key ?? generateProductKey(),
|
||||
sub: body.sub ?? generateSub(),
|
||||
pid: body.pid ?? 'dnd_player',
|
||||
maxDevices: body.maxDevices ?? 3,
|
||||
expiresAtSec: body.expiresAtSec ?? now + 86400 * 365,
|
||||
...(expiry.expiresAtSec != null
|
||||
? { expiresAtSec: expiry.expiresAtSec }
|
||||
: { validDays: expiry.validDays }),
|
||||
};
|
||||
if (!entry.key || !entry.sub) return json(res, 400, { error: 'invalid_entry' });
|
||||
const data = readData();
|
||||
|
||||
Reference in New Issue
Block a user