/** * License Issuer — 生成签名 License 文件 * * 用法: * import { issueLicense } from '@gloablehive/license'; * const file = issueLicense({ merchantId: 'M001', merchantName: '水果店', plan: 'basic' }, SECRET); * fs.writeFileSync('license.json', JSON.stringify(file, null, 2)); */ import * as crypto from 'crypto'; import { signPayload, getMachineId } from './license-guard.js'; import type { LicenseFile, LicensePayload, LicensePlan } from './types.js'; import { PLAN_PRESETS } from './types.js'; export interface IssueOptions { /** 商户 ID */ merchantId: string; /** 商户名称 */ merchantName: string; /** 套餐 */ plan: LicensePlan; /** 有效天数 (默认 365) */ validDays?: number; /** 是否绑定当前机器 (默认 false) */ bindMachine?: boolean; /** 自定义功能覆盖 */ featuresOverride?: Partial; /** 自定义配额覆盖 */ quotasOverride?: Partial; } /** * 生成签名的 License 文件对象 */ export function issueLicense(opts: IssueOptions, secret: string): LicenseFile { const preset = PLAN_PRESETS[opts.plan]; const now = new Date(); const validDays = opts.validDays ?? 365; const expires = new Date(now.getTime() + validDays * 24 * 60 * 60 * 1000); const payload: LicensePayload = { id: crypto.randomUUID(), merchantId: opts.merchantId, merchantName: opts.merchantName, plan: opts.plan, features: { ...preset.features, ...opts.featuresOverride }, quotas: { ...preset.quotas, ...opts.quotasOverride }, issuedAt: now.toISOString(), expiresAt: expires.toISOString(), machineId: opts.bindMachine ? getMachineId() : undefined, }; const signature = signPayload(payload, secret); return { version: 1, payload, signature, }; }