openvibers/openvibe.host Commits

Host: a plan page links its provider (Visit provider) (#77)

OP OpenVibers committed 12 Oct 2026 · e093758fe9 · 2 files, +2 −1

A plan page's price card had no way to the provider: the plan query never selected the provider's homepage, so `visit()` had nothing to link. The page now shows "Visit provider" (or the marked affiliate link with its disclosure, when the provider has one). Test: `catalog-public.test.js`.

Changes

ddiff --git a/server/catalog/store.js b/server/catalog/store.js
iindex fe0e7c5..b40e762 100644
---- a/server/catalog/store.js
++++ b/server/catalog/store.js
@@@ -146,7 +146,7 @@ function createCatalog({ store, community = null, log = console }) {
return { plans: rows.slice(0, 50).map(planOut), next_cursor: rows.length > 50 ? String(offset + 50) : null };
}
async function plan(id) {
-- const p = await db.prepare("SELECT p.*, v.slug AS provider_slug, v.name AS provider_name, v.affiliate_url, v.affiliate_note FROM catalog_plans p JOIN catalog_providers v ON v.id = p.provider_id WHERE p.id = ? AND p.status = 'active' AND v.status = 'active'").get(id);
++ const p = await db.prepare("SELECT p.*, v.slug AS provider_slug, v.name AS provider_name, v.homepage_url, v.affiliate_url, v.affiliate_note FROM catalog_plans p JOIN catalog_providers v ON v.id = p.provider_id WHERE p.id = ? AND p.status = 'active' AND v.status = 'active'").get(id);
if (!p) missing();
return { ...planOut(p), price_history: (await db.prepare('SELECT * FROM catalog_price_changes WHERE plan_id = ? ORDER BY observed_at DESC').all(id)).map((h) => ({ ...h, checked_at: new Date(Number(h.observed_at)).toISOString(), source_url: p.source_url })) };
}
ddiff --git a/test/catalog-public.test.js b/test/catalog-public.test.js
iindex 7c94a98..ea5a5cb 100644
---- a/test/catalog-public.test.js
++++ b/test/catalog-public.test.js
@@@ -51,6 +51,7 @@ const { boot, check, done } = require('./stageb/boot');
assert.ok(types(plans.text).includes('ItemList'));
const planPage = await t.api('GET', `/catalog/plans/${cheap.id}`);
const product = ld(planPage.text).find((x) => x['@type'] === 'Product');
++ assert.match(planPage.text, /class="hc-visit"/, 'a plan page offers a way to the provider');
assert.ok(product && product.offers.price === '4.00' && product.offers.priceCurrency === 'USD' && product.offers.priceSpecification.unitText === 'MONTH');
assert.ok(!types((await t.api('GET', '/catalog?min_vcpu=2')).text).includes('ItemList'), 'a filtered view carries no JSON-LD');
const promos = await t.api('GET', '/api/v1/catalog/promos'); assert.strictEqual(promos.json().promos.length, 1);