подготовка к билду
This commit is contained in:
@@ -6,6 +6,8 @@ import { ipcChannels } from '../../shared/ipc/contracts';
|
||||
import { EULA_CURRENT_VERSION } from '../../shared/license/eulaVersion';
|
||||
import type { LicenseSnapshot } from '../../shared/license/licenseSnapshot';
|
||||
import type { LicensePayloadV1 } from '../../shared/license/payloadV1';
|
||||
import { isDndProductKey } from '../../shared/license/productKey';
|
||||
import { normalizeLicenseTokenInput } from '../../shared/license/tokenFormat';
|
||||
|
||||
import { getOrCreateDeviceId } from './deviceId';
|
||||
import { licenseEncryptedPath, preferencesPath } from './paths';
|
||||
@@ -77,6 +79,40 @@ export class LicenseService {
|
||||
}
|
||||
}
|
||||
|
||||
/** База для `POST /v1/activate` (и при желании совпадает с сервером отзыва). */
|
||||
private resolveLicenseActivateBaseUrl(): string {
|
||||
const raw = process.env.DND_LICENSE_STATUS_URL?.trim();
|
||||
if (raw) return raw.endsWith('/') ? raw : `${raw}/`;
|
||||
return 'https://license.mailib.ru/';
|
||||
}
|
||||
|
||||
private async activateWithProductKey(productKey: string): Promise<string> {
|
||||
const base = this.resolveLicenseActivateBaseUrl();
|
||||
const url = new URL('v1/activate', base);
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify({ productKey: productKey.trim(), deviceId: this.deviceId }),
|
||||
signal: AbortSignal.timeout(20_000),
|
||||
});
|
||||
const text = await res.text();
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(text) as unknown;
|
||||
} catch {
|
||||
throw new Error(`LICENSE_ACTIVATE_FAILED:${text.slice(0, 200)}`);
|
||||
}
|
||||
const obj = parsed as { token?: string; error?: string; message?: string };
|
||||
if (!res.ok) {
|
||||
throw new Error(`LICENSE_ACTIVATE_FAILED:${obj.error ?? obj.message ?? String(res.status)}`);
|
||||
}
|
||||
const token = obj.token;
|
||||
if (!token || typeof token !== 'string') {
|
||||
throw new Error('LICENSE_ACTIVATE_FAILED:token_missing');
|
||||
}
|
||||
return normalizeLicenseTokenInput(token);
|
||||
}
|
||||
|
||||
private async maybeRefreshRemoteRevocation(payload: LicensePayloadV1): Promise<void> {
|
||||
const base = process.env.DND_LICENSE_STATUS_URL?.trim();
|
||||
if (!base) return;
|
||||
@@ -191,11 +227,14 @@ export class LicenseService {
|
||||
return this.getStatusSync();
|
||||
}
|
||||
|
||||
setToken(token: string): LicenseSnapshot {
|
||||
async setToken(token: string): Promise<LicenseSnapshot> {
|
||||
if (this.isSkipLicense()) {
|
||||
return this.getStatusSync();
|
||||
}
|
||||
const trimmed = token.trim();
|
||||
let trimmed = normalizeLicenseTokenInput(token);
|
||||
if (isDndProductKey(trimmed)) {
|
||||
trimmed = await this.activateWithProductKey(trimmed);
|
||||
}
|
||||
const nowSec = Math.floor(Date.now() / 1000);
|
||||
const v = verifyLicenseToken(trimmed, { nowSec, deviceId: this.deviceId });
|
||||
if (!v.ok) {
|
||||
|
||||
@@ -54,3 +54,29 @@ void test('verifyLicenseToken: неверное устройство', () => {
|
||||
if (bad.ok) assert.fail('expected failure');
|
||||
assert.equal(bad.reason, 'wrong_device');
|
||||
});
|
||||
|
||||
void test('verifyLicenseToken: токен с переносами строк после копирования', () => {
|
||||
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
|
||||
const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
|
||||
const licensePayload = {
|
||||
v: 1 as const,
|
||||
sub: 'lic_wrap',
|
||||
pid: 'dnd_player',
|
||||
iat: 100,
|
||||
exp: 2_000_000_000,
|
||||
did: null as string | null,
|
||||
};
|
||||
const body = canonicalJson(licensePayload);
|
||||
const sig = sign(null, Buffer.from(body, 'utf8'), privateKey);
|
||||
const token = joinSignedLicenseToken(body, new Uint8Array(sig.buffer, sig.byteOffset, sig.byteLength));
|
||||
const mid = Math.max(1, Math.floor(token.length / 2));
|
||||
const messy = `${token.slice(0, mid)}\n${token.slice(mid, mid + 3)} \r\n ${token.slice(mid + 3)}`;
|
||||
|
||||
const ok = verifyLicenseToken(messy, {
|
||||
nowSec: 1_700_000_000,
|
||||
deviceId: 'any',
|
||||
publicKeyOverrideSpkiDerB64: pubB64,
|
||||
});
|
||||
if (!ok.ok) assert.fail(`expected ok, got ${ok.reason}`);
|
||||
assert.equal(ok.payload.sub, 'lic_wrap');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user