85 lines
3.2 KiB
JavaScript
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 };
|
|
}
|