137 lines
3.3 KiB
JavaScript
137 lines
3.3 KiB
JavaScript
/**
|
|
* @fileoverview Tenant and API key persistence.
|
|
* @module store/tenants
|
|
*/
|
|
|
|
import { getStore, persist } from './db.js';
|
|
import { generateApiKey } from '../lib/tokens.js';
|
|
import { PLAN_LIMITS } from '../config.js';
|
|
import { createDebugger } from '../debug/logger.js';
|
|
|
|
const log = createDebugger('billing');
|
|
|
|
/**
|
|
* @typedef {Object} Tenant
|
|
* @property {string} id
|
|
* @property {string} name
|
|
* @property {string} plan
|
|
* @property {string} veraeUsername
|
|
* @property {string} veraePassword
|
|
* @property {object|null} contract
|
|
* @property {object} [metadata]
|
|
* @property {string} createdAt
|
|
*/
|
|
|
|
/**
|
|
* @param {string} tenantId
|
|
* @returns {Tenant|null}
|
|
*/
|
|
export function getTenant(tenantId) {
|
|
return getStore().tenants[tenantId] ?? null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} apiKey
|
|
* @returns {Tenant|null}
|
|
*/
|
|
export function getTenantByApiKey(apiKey) {
|
|
const store = getStore();
|
|
const tenantId = store.apiKeys[apiKey];
|
|
return tenantId ? store.tenants[tenantId] ?? null : null;
|
|
}
|
|
|
|
/**
|
|
* @returns {Tenant[]}
|
|
*/
|
|
export function listTenants() {
|
|
return Object.values(getStore().tenants);
|
|
}
|
|
|
|
/**
|
|
* @param {Tenant} tenant
|
|
* @returns {Tenant}
|
|
*/
|
|
export function upsertTenant(tenant) {
|
|
const store = getStore();
|
|
store.tenants[tenant.id] = tenant;
|
|
persist();
|
|
log.debug('tenant upserted', { tenantId: tenant.id, plan: tenant.plan });
|
|
return tenant;
|
|
}
|
|
|
|
/**
|
|
* Create a tenant and bind a new API key.
|
|
*
|
|
* @param {object} params
|
|
* @param {string} params.id
|
|
* @param {string} params.name
|
|
* @param {string} [params.plan='free']
|
|
* @param {string} params.veraeUsername
|
|
* @param {string} params.veraePassword
|
|
* @param {object|null} [params.contract=null]
|
|
* @param {string} [params.apiKey]
|
|
* @param {object} [params.metadata]
|
|
* @returns {{ tenant: Tenant, apiKey: string }}
|
|
*/
|
|
export function createTenant({
|
|
id,
|
|
name,
|
|
plan = 'free',
|
|
veraeUsername,
|
|
veraePassword,
|
|
contract = null,
|
|
apiKey = generateApiKey(),
|
|
metadata = {},
|
|
}) {
|
|
const store = getStore();
|
|
/** @type {Tenant} */
|
|
const tenant = {
|
|
id,
|
|
name,
|
|
plan,
|
|
veraeUsername,
|
|
veraePassword,
|
|
contract,
|
|
metadata,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
store.tenants[id] = tenant;
|
|
store.apiKeys[apiKey] = id;
|
|
persist();
|
|
|
|
log.info('tenant created', { tenantId: id, plan, audience: metadata.audience });
|
|
return { tenant, apiKey };
|
|
}
|
|
|
|
/**
|
|
* Resolve effective limits for a tenant (plan defaults or enterprise contract).
|
|
*
|
|
* @param {Tenant} tenant
|
|
* @returns {{
|
|
* timestamps: number|null,
|
|
* verifications: number|null,
|
|
* batch: boolean,
|
|
* batchMaxItems: number|null,
|
|
* requestsPerMinute: number,
|
|
* allowOverage: boolean,
|
|
* overageRates: object
|
|
* }}
|
|
*/
|
|
export function resolveLimits(tenant) {
|
|
if (tenant.plan === 'enterprise' && tenant.contract) {
|
|
return {
|
|
timestamps: tenant.contract.includedTimestamps ?? null,
|
|
verifications: tenant.contract.includedVerifications ?? null,
|
|
batch: tenant.contract.batch !== false,
|
|
batchMaxItems: tenant.contract.batchMaxItems ?? null,
|
|
requestsPerMinute:
|
|
tenant.contract.requestsPerMinute ?? PLAN_LIMITS.enterprise.requestsPerMinute,
|
|
allowOverage: tenant.contract.allowOverage ?? false,
|
|
overageRates: tenant.contract.overageRates ?? {},
|
|
};
|
|
}
|
|
|
|
const base = PLAN_LIMITS[tenant.plan] ?? PLAN_LIMITS.free;
|
|
return { ...base, allowOverage: false, overageRates: {} };
|
|
}
|