diff --git a/README.md b/README.md index b5a06a5b..24a042d6 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,11 @@ latest styles. - Central place for instance-specific values like the Super Admin npub and the default whitelist-only mode setting. Update the documented exports here when preparing a new deployment. - - Tune `PLATFORM_FEE_PERCENT` (0–100) to keep a percentage of Lightning tips - and set `PLATFORM_LUD16_OVERRIDE` when you need a deployment-wide fallback - Lightning address. Leave the fee at `0` to pass through every satoshi. + - Tune `PLATFORM_FEE_PERCENT` (0–100) to keep a percentage of Lightning tips. + When the fee is positive, BitVid routes the platform’s split to + `PLATFORM_LUD16_OVERRIDE`, so set it to the Lightning address that should + receive the sats (or publish a `lud16` on the Super Admin profile). Leave + the fee at `0` to pass through every satoshi. - Populate `DEFAULT_RELAY_URLS_OVERRIDE` with WSS URLs to replace the bundled relay bootstrap list. Keep it empty to stick with the upstream defaults. - **`config.js`**: diff --git a/config/instance-config.js b/config/instance-config.js index eec179e2..7c565979 100644 --- a/config/instance-config.js +++ b/config/instance-config.js @@ -38,8 +38,11 @@ export const PLATFORM_FEE_PERCENT = 0; * Lightning address to fall back to when authors omit their own `lud16`. * * Supply a string like `"tips@example.com"` to force a deployment-wide - * fallback Lightning address, or leave the value as `null` to respect the - * creator’s configured profile metadata. + * fallback Lightning address. When `PLATFORM_FEE_PERCENT` is greater than 0, + * this override also acts as the Lightning target for the platform’s split. + * Leave the value as `null` to respect the creator’s metadata and rely on the + * Super Admin profile publishing a `lud16` so BitVid still knows where to route + * fees when they are enabled. */ export const PLATFORM_LUD16_OVERRIDE = null; diff --git a/config/validate-config.js b/config/validate-config.js index fb9b78b3..8d06219e 100644 --- a/config/validate-config.js +++ b/config/validate-config.js @@ -3,16 +3,20 @@ // Runtime validation helpers for instance-level configuration // ----------------------------------------------------------------------------- -import { ADMIN_SUPER_NPUB, PLATFORM_FEE_PERCENT } from "./instance-config.js"; +import { + ADMIN_SUPER_NPUB, + PLATFORM_FEE_PERCENT, + PLATFORM_LUD16_OVERRIDE, +} from "./instance-config.js"; -function assertSuperAdminConfigured() { - if (typeof ADMIN_SUPER_NPUB !== "string") { +function assertSuperAdminConfigured(value) { + if (typeof value !== "string") { throw new Error( "ADMIN_SUPER_NPUB must be a string exported from config/instance-config.js." ); } - const trimmed = ADMIN_SUPER_NPUB.trim(); + const trimmed = value.trim(); if (!trimmed) { throw new Error( "ADMIN_SUPER_NPUB is required. Populate it with the Super Admin npub in config/instance-config.js." @@ -24,26 +28,58 @@ function assertSuperAdminConfigured() { "ADMIN_SUPER_NPUB should be a bech32 npub (e.g., starting with 'npub')." ); } + return trimmed; } -function assertPlatformFeeInRange() { - const value = - typeof PLATFORM_FEE_PERCENT === "number" - ? PLATFORM_FEE_PERCENT - : Number.parseFloat(PLATFORM_FEE_PERCENT); +function assertPlatformFeeInRange(value) { + const numericValue = + typeof value === "number" ? value : Number.parseFloat(value); - if (!Number.isFinite(value)) { + if (!Number.isFinite(numericValue)) { throw new Error( "PLATFORM_FEE_PERCENT must be a finite number between 0 and 100." ); } - if (value < 0 || value > 100) { + if (numericValue < 0 || numericValue > 100) { throw new Error("PLATFORM_FEE_PERCENT must be between 0 and 100 inclusive."); } + + return numericValue; } -export function validateInstanceConfig() { - assertSuperAdminConfigured(); - assertPlatformFeeInRange(); +function getPlatformLud16Override(value) { + if (typeof value !== "string") { + return ""; + } + + return value.trim(); +} + +function resolveConfig(overrides = {}) { + return { + adminSuperNpub: + overrides.ADMIN_SUPER_NPUB ?? ADMIN_SUPER_NPUB, + platformFeePercent: + overrides.PLATFORM_FEE_PERCENT ?? PLATFORM_FEE_PERCENT, + platformLud16Override: + overrides.PLATFORM_LUD16_OVERRIDE ?? PLATFORM_LUD16_OVERRIDE, + }; +} + +export function validateInstanceConfig(overrides) { + const config = resolveConfig(overrides); + assertSuperAdminConfigured(config.adminSuperNpub); + const platformFeePercent = assertPlatformFeeInRange( + config.platformFeePercent + ); + const trimmedOverride = getPlatformLud16Override( + config.platformLud16Override + ); + + if (platformFeePercent > 0 && !trimmedOverride) { + throw new Error( + "PLATFORM_FEE_PERCENT is positive but PLATFORM_LUD16_OVERRIDE is empty. Set PLATFORM_LUD16_OVERRIDE to the Lightning address that should receive the platform's split, or publish a lud16 value on the Super Admin profile so BitVid can route platform fees." + ); + } } diff --git a/tests/validate-config.test.mjs b/tests/validate-config.test.mjs new file mode 100644 index 00000000..0244a58e --- /dev/null +++ b/tests/validate-config.test.mjs @@ -0,0 +1,21 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { validateInstanceConfig } from "../config/validate-config.js"; + +test( + "validateInstanceConfig throws when platform fee is positive without a platform override", + () => { + const overrides = { + ADMIN_SUPER_NPUB: + "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + PLATFORM_FEE_PERCENT: 5, + PLATFORM_LUD16_OVERRIDE: " ", + }; + + assert.throws(() => validateInstanceConfig(overrides), { + message: + "PLATFORM_FEE_PERCENT is positive but PLATFORM_LUD16_OVERRIDE is empty. Set PLATFORM_LUD16_OVERRIDE to the Lightning address that should receive the platform's split, or publish a lud16 value on the Super Admin profile so BitVid can route platform fees.", + }); + } +);