Files
2026-10-29 11:57:29 +01:00

85 lines
3.2 KiB
JavaScript

// Generated, do not edit. sha256:1228a952eacb4d1d9d3f5acbdbe35af2071a6bcad5057d2b1aecfd3b41975c0e
import { readFileSync, writeFileSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { CONFIG } from '../../shared/config.js';
import { parseLicense, verifySignature, coversVersion, appVersion, readLicense } from '../license.mjs';
import { installDir } from '../portfolios.mjs';
import { marketBuyerLicenses, marketServiceUrl, marketSale, marketPlaceOrder, marketOrderStatus } from '../market-service.mjs';
const HEX64 = /^[0-9a-f]{64}$/;
const buyerFile = () => join(installDir(), CONFIG.LICENSE.BUYER_FILE_NAME);
function rememberedBuyer() {
try {
const key = JSON.parse(readFileSync(buyerFile(), 'utf8'))?.buyer;
return HEX64.test(String(key)) ? key : null;
} catch {
return null;
}
}
function signerOf(signature) {
const m = /^Nostr ([A-Za-z0-9+/=]+)$/.exec(String(signature ?? ''));
if (!m) throw new Error('A sale call carries an "Authorization: Nostr <base64 event>" header.');
let pubkey;
try { pubkey = JSON.parse(Buffer.from(m[1], 'base64').toString('utf8'))?.pubkey; } catch { pubkey = null; }
if (!HEX64.test(String(pubkey))) throw new Error('The signed event carries no 64-hex pubkey.');
return pubkey;
}
export async function licenseShop() {
return { serviceUrl: await marketServiceUrl(), buyer: rememberedBuyer() };
}
export async function buyerLicenses(signature) {
const signer = signerOf(signature);
const list = await marketBuyerLicenses(signature);
writeFileSync(buyerFile(), `${JSON.stringify({ buyer: signer }, null, 2)}\n`);
const judge = judgeAgainstInstalled();
return list.map(judge);
}
function judgeAgainstInstalled() {
const version = appVersion();
const current = readLicense();
const now = current.state === 'valid' ? current.license : null;
return (file) => {
const text = JSON.stringify(file);
try {
const parsed = parseLicense(text);
const l = parsed.license;
const installed = !!now && ['buyer', 'level', 'issued', 'majorFrom', 'majorTo'].every(k => String(now[k]) === String(l[k]));
return {
file: text, level: l.level, issued: l.issued, majorFrom: l.majorFrom, majorTo: l.majorTo,
valid: verifySignature(parsed), covers: coversVersion(l, version), installed,
};
} catch {
return { file: text, valid: false, unreadable: true };
}
};
}
const ORDER_ID = /^[0-9a-f]{32}$/;
export async function saleInfo() {
return marketSale();
}
export async function placeOrder(signature, body = '{}') {
signerOf(signature);
if (typeof body !== 'string') throw new Error('The body of an order is the signed JSON text.');
return marketPlaceOrder(signature, body);
}
export async function orderStatus(orderId, readKey) {
if (!ORDER_ID.test(String(orderId))) throw new Error('An order id is 32 hex characters.');
if (!HEX64.test(String(readKey))) throw new Error('A read key is 64 hex characters.');
const { order, license } = await marketOrderStatus(orderId, readKey);
return { order, license: license ? judgeAgainstInstalled()(license) : null };
}
export async function forgetBuyer() {
rmSync(buyerFile(), { force: true });
return { buyer: null };
}